diff options
author | Benjamin Pujol <benjamin.pujol@intel.com> | 2015-04-10 10:13:52 +0000 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2015-04-10 10:13:52 +0000 |
commit | 8dfad1714fb3ea1ffca032c2163cb50aedfa8cad (patch) | |
tree | 28260902d4c340ac0cfb51d50913dfdee45e6e3b | |
parent | df929beee6fa2a3c5b6125b7fd3087426de4150e (diff) | |
parent | b6cd081e90a23a60aa6488f40c07f86fac5a48d6 (diff) | |
download | hwcomposer-8dfad1714fb3ea1ffca032c2163cb50aedfa8cad.tar.gz |
am b6cd081e: am 8b0063f7: First hwcomposer code drop for marvin
* commit 'b6cd081e90a23a60aa6488f40c07f86fac5a48d6':
First hwcomposer code drop for marvin
142 files changed, 23822 insertions, 0 deletions
diff --git a/merrifield/Android.mk b/merrifield/Android.mk new file mode 100644 index 0000000..8dbd160 --- /dev/null +++ b/merrifield/Android.mk @@ -0,0 +1,26 @@ +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +LOCAL_PATH := $(call my-dir) + +LOCAL_ROOT_PATH := $(call my-dir) + +ifeq ($(INTEL_HWC_MERRIFIELD),true) +include $(LOCAL_PATH)/platforms/merrifield/Android.mk +endif + +ifeq ($(INTEL_HWC_MOOREFIELD),true) +include $(LOCAL_PATH)/platforms/merrifield_plus/Android.mk +endif + diff --git a/merrifield/common/base/DisplayAnalyzer.cpp b/merrifield/common/base/DisplayAnalyzer.cpp new file mode 100755 index 0000000..5da7403 --- /dev/null +++ b/merrifield/common/base/DisplayAnalyzer.cpp @@ -0,0 +1,907 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <HwcTrace.h> +#include <IDisplayDevice.h> +#include <DisplayQuery.h> +#include <BufferManager.h> +#include <DisplayPlaneManager.h> +#include <Hwcomposer.h> +#include <DisplayAnalyzer.h> +#include <cutils/properties.h> +#include <GraphicBuffer.h> +#include <ExternalDevice.h> +#ifdef INTEL_WIDI_MERRIFIELD +#include <VirtualDevice.h> +#endif + +namespace android { +namespace intel { + +DisplayAnalyzer::DisplayAnalyzer() + : mInitialized(false), + mVideoExtModeEnabled(true), + mVideoExtModeEligible(false), + mVideoExtModeActive(false), + mBlankDevice(false), + mOverlayAllowed(true), + mActiveInputState(true), + mIgnoreVideoSkipFlag(false), + mProtectedVideoSession(false), + mCachedNumDisplays(0), + mCachedDisplays(0), + mPendingEvents(), + mEventMutex(), + mEventHandledCondition() +{ +} + +DisplayAnalyzer::~DisplayAnalyzer() +{ +} + +bool DisplayAnalyzer::initialize() +{ + // by default video extended mode is enabled + char prop[PROPERTY_VALUE_MAX]; + if (property_get("hwc.video.extmode.enable", prop, "1") > 0) { + mVideoExtModeEnabled = atoi(prop) ? true : false; + } + mVideoExtModeEligible = false; + mVideoExtModeActive = false; + mBlankDevice = false; + mOverlayAllowed = true; + mActiveInputState = true; + mIgnoreVideoSkipFlag = false; + mProtectedVideoSession = false; + mCachedNumDisplays = 0; + mCachedDisplays = 0; + mPendingEvents.clear(); + mVideoStateMap.clear(); + mInitialized = true; + + return true; +} + +void DisplayAnalyzer::deinitialize() +{ + mPendingEvents.clear(); + mVideoStateMap.clear(); + mInitialized = false; +} + +void DisplayAnalyzer::analyzeContents( + size_t numDisplays, hwc_display_contents_1_t** displays) +{ + // cache and use them only in this context during analysis + mCachedNumDisplays = numDisplays; + mCachedDisplays = displays; + + handlePendingEvents(); + + if (mVideoExtModeEnabled) { + handleVideoExtMode(); + } + + if (mBlankDevice) { + // this will make sure device is blanked after geometry changes. + // blank event is only processed once + blankSecondaryDevice(); + } +} + +void DisplayAnalyzer::handleVideoExtMode() +{ + bool eligible = mVideoExtModeEligible; + checkVideoExtMode(); + if (eligible == mVideoExtModeEligible) { + if (mVideoExtModeActive) { + // need to mark all layers + setCompositionType(0, HWC_OVERLAY, false); + } + return; + } + + if (mVideoExtModeEligible) { + if (mActiveInputState) { + VTRACE("input is active"); + } else { + enterVideoExtMode(); + } + } else { + exitVideoExtMode(); + } +} + +void DisplayAnalyzer::checkVideoExtMode() +{ + if (mVideoStateMap.size() != 1) { + mVideoExtModeEligible = false; + return; + } + + Hwcomposer *hwc = &Hwcomposer::getInstance(); + + ExternalDevice *eDev = static_cast<ExternalDevice *>(hwc->getDisplayDevice(IDisplayDevice::DEVICE_EXTERNAL)); +#ifdef INTEL_WIDI_MERRIFIELD + VirtualDevice *vDev = static_cast<VirtualDevice *>(hwc->getDisplayDevice(IDisplayDevice::DEVICE_VIRTUAL)); + + if ((!eDev || !eDev->isConnected()) && (!vDev || !vDev->isFrameServerActive())) { + mVideoExtModeEligible = false; + return; + } +#else + if (!eDev || !eDev->isConnected()) { + mVideoExtModeEligible = false; + return; + } +#endif /*INTEL_WIDI_MERRIFIELD*/ + + bool geometryChanged = false; + int activeDisplays = 0; + + hwc_display_contents_1_t *content = NULL; + for (int i = 0; i < (int)mCachedNumDisplays; i++) { + content = mCachedDisplays[i]; + if (content == NULL) { + continue; + } + activeDisplays++; + if (content->flags & HWC_GEOMETRY_CHANGED) { + geometryChanged = true; + } + } + + if (activeDisplays <= 1) { + mVideoExtModeEligible = false; + return; + } + + // video state update event may come later than geometry change event. + // in that case, video extended mode is not detected properly. +#if 0 + if (geometryChanged == false) { + // use previous analysis result + return; + } +#endif + // reset eligibility of video extended mode + mVideoExtModeEligible = false; + + // check if there is video layer in the primary device + content = mCachedDisplays[0]; + if (content == NULL) { + return; + } + + buffer_handle_t videoHandle = 0; + bool videoLayerExist = false; + bool videoFullScreenOnPrimary = false; + bool isVideoLayerSkipped = false; + + // exclude the frame buffer target layer + for (int j = 0; j < (int)content->numHwLayers - 1; j++) { + videoLayerExist = isVideoLayer(content->hwLayers[j]); + if (videoLayerExist) { + if ((content->hwLayers[j].flags & HWC_SKIP_LAYER)) { + isVideoLayerSkipped = true; + } + videoHandle = content->hwLayers[j].handle; + videoFullScreenOnPrimary = isVideoFullScreen(0, content->hwLayers[j]); + break; + } + } + + if (videoLayerExist == false) { + // no video layer is found in the primary layer + return; + } + + // check whether video layer exists in external device or virtual device + // TODO: video may exist in virtual device but no in external device or vice versa + // TODO: multiple video layers are not addressed here + for (int i = 1; i < (int)mCachedNumDisplays; i++) { + content = mCachedDisplays[i]; + if (content == NULL) { + continue; + } + + // exclude the frame buffer target layer + for (int j = 0; j < (int)content->numHwLayers - 1; j++) { + if (content->hwLayers[j].handle == videoHandle) { + isVideoLayerSkipped |= (content->hwLayers[j].flags & HWC_SKIP_LAYER); + VTRACE("video layer exists in device %d", i); + if (isVideoLayerSkipped || videoFullScreenOnPrimary){ + VTRACE("Video ext mode eligible, %d, %d", + isVideoLayerSkipped, videoFullScreenOnPrimary); + mVideoExtModeEligible = true; + } else { + mVideoExtModeEligible = isVideoFullScreen(i, content->hwLayers[j]); + } + return; + } + } + } +} + +bool DisplayAnalyzer::isVideoExtModeActive() +{ + return mVideoExtModeActive; +} + +bool DisplayAnalyzer::isVideoExtModeEnabled() +{ +#if 1 + // enable it for run-time debugging purpose. + char prop[PROPERTY_VALUE_MAX]; + if (property_get("hwc.video.extmode.enable", prop, "1") > 0) { + mVideoExtModeEnabled = atoi(prop) ? true : false; + } + ITRACE("video extended mode enabled: %d", mVideoExtModeEnabled); +#endif + + return mVideoExtModeEnabled; +} + +bool DisplayAnalyzer::isVideoLayer(hwc_layer_1_t &layer) +{ + bool ret = false; + BufferManager *bm = Hwcomposer::getInstance().getBufferManager(); + if (!layer.handle) { + return false; + } + DataBuffer *buffer = bm->lockDataBuffer(layer.handle); + if (!buffer) { + ETRACE("failed to get buffer"); + } else { + ret = DisplayQuery::isVideoFormat(buffer->getFormat()); + bm->unlockDataBuffer(buffer); + } + return ret; +} + +bool DisplayAnalyzer::isVideoFullScreen(int device, hwc_layer_1_t &layer) +{ + IDisplayDevice *displayDevice = Hwcomposer::getInstance().getDisplayDevice(device); + if (!displayDevice) { + return false; + } + int width = 0, height = 0; + if (!displayDevice->getDisplaySize(&width, &height)) { + return false; + } + + VTRACE("video left %d, right %d, top %d, bottom %d, device width %d, height %d", + layer.displayFrame.left, layer.displayFrame.right, + layer.displayFrame.top, layer.displayFrame.bottom, + width, height); + + // full-screen defintion: + // width of target display frame == width of target device, with 1 pixel of tolerance, or + // Height of target display frame == height of target device, with 1 pixel of tolerance, or + // width * height of display frame > 90% of width * height of display device, or + // any of above condition is met on either primary display or secondary display + int dstW = layer.displayFrame.right - layer.displayFrame.left; + int dstH = layer.displayFrame.bottom - layer.displayFrame.top; + + if (abs(dstW - width) > 1 && + abs(dstH - height) > 1 && + dstW * dstH * 10 < width * height * 9) { + VTRACE("video is not full-screen"); + return false; + } + return true; +} + +bool DisplayAnalyzer::isOverlayAllowed() +{ + return mOverlayAllowed; +} + +int DisplayAnalyzer::getVideoInstances() +{ + return (int)mVideoStateMap.size(); +} + +void DisplayAnalyzer::postHotplugEvent(bool connected) +{ + if (!connected) { + // enable vsync on the primary device immediately + Hwcomposer::getInstance().getVsyncManager()->enableDynamicVsync(true); + } + + // handle hotplug event (vsync switch) asynchronously + Event e; + e.type = HOTPLUG_EVENT; + e.bValue = connected; + postEvent(e); + Hwcomposer::getInstance().invalidate(); +} + +void DisplayAnalyzer::postVideoEvent(int instanceID, int state) +{ + Event e; + e.type = VIDEO_EVENT; + e.videoEvent.instanceID = instanceID; + e.videoEvent.state = state; + postEvent(e); + if ((state == VIDEO_PLAYBACK_STARTING) || + (state == VIDEO_PLAYBACK_STOPPING && mProtectedVideoSession)) { + Hwcomposer::getInstance().invalidate(); + mOverlayAllowed = false; + hwc_display_contents_1_t *content = NULL; + for (int i = 0; i < (int)mCachedNumDisplays; i++) { + setCompositionType(i, HWC_FRAMEBUFFER, true); + } + // wait for up to 100ms until overlay is disabled. + int loop = 0; + while (loop++ < 6) { + if (Hwcomposer::getInstance().getPlaneManager()->isOverlayPlanesDisabled()) + break; + usleep(16700); + } + if (loop >= 6) { + WTRACE("timeout disabling overlay "); + } + } +} + +void DisplayAnalyzer::postBlankEvent(bool blank) +{ + Event e; + e.type = BLANK_EVENT; + e.bValue = blank; + postEvent(e); + Hwcomposer::getInstance().invalidate(); +} + +void DisplayAnalyzer::postInputEvent(bool active) +{ + Event e; + e.type = INPUT_EVENT; + e.bValue = active; + postEvent(e); + Hwcomposer::getInstance().invalidate(); +} + +void DisplayAnalyzer::postIdleEntryEvent(void) +{ + Event e; + e.type = IDLE_ENTRY_EVENT; + e.nValue = 0; + postEvent(e); +} + +void DisplayAnalyzer::postEvent(Event& e) +{ + Mutex::Autolock lock(mEventMutex); + mPendingEvents.add(e); +} + +bool DisplayAnalyzer::getEvent(Event& e) +{ + Mutex::Autolock lock(mEventMutex); + if (mPendingEvents.size() == 0) { + return false; + } + e = mPendingEvents[0]; + mPendingEvents.removeAt(0); + return true; +} + +void DisplayAnalyzer::handlePendingEvents() +{ + // handle one event per analysis to avoid blocking surface flinger + // some event may take lengthy time to process + Event e; + if (!getEvent(e)) { + return; + } + + switch (e.type) { + case HOTPLUG_EVENT: + handleHotplugEvent(e.bValue); + break; + case BLANK_EVENT: + handleBlankEvent(e.bValue); + break; + case VIDEO_EVENT: + handleVideoEvent(e.videoEvent.instanceID, e.videoEvent.state); + break; + case TIMING_EVENT: + handleTimingEvent(); + break; + case INPUT_EVENT: + handleInputEvent(e.bValue); + break; + case DPMS_EVENT: + handleDpmsEvent(e.nValue); + break; + case IDLE_ENTRY_EVENT: + handleIdleEntryEvent(e.nValue); + break; + case IDLE_EXIT_EVENT: + handleIdleExitEvent(); + break; + case VIDEO_CHECK_EVENT: + handleVideoCheckEvent(); + break; + } +} + +void DisplayAnalyzer::handleHotplugEvent(bool connected) +{ + Hwcomposer *hwc = &Hwcomposer::getInstance(); + if (connected) { + if (mVideoStateMap.size() == 1) { + // Some video apps wouldn't update video state again when plugin HDMI + // and fail to reset refresh rate + ExternalDevice *dev = NULL; + dev = (ExternalDevice *)hwc->getDisplayDevice(IDisplayDevice::DEVICE_EXTERNAL); + if (!dev || !dev->isConnected()) { + ITRACE("External device isn't connected"); + return; + } + if (hwc->getMultiDisplayObserver()->isExternalDeviceTimingFixed()) { + VTRACE("Timing of external device is fixed."); + return; + } + VideoSourceInfo info; + int instanceID = mVideoStateMap.keyAt(0); + status_t err = hwc->getMultiDisplayObserver()->getVideoSourceInfo( + instanceID, &info); + if (err == NO_ERROR) { + int hz = dev->getRefreshRate(); + if (hz > 0 && info.frameRate > 0 && hz != info.frameRate) { + ITRACE("Old Hz %d, new one %d", hz, info.frameRate); + dev->setRefreshRate(info.frameRate); + } else + WTRACE("Old Hz %d is invalid, %d", hz, info.frameRate); + } + } + } else { + if (mVideoStateMap.size() == 1) { + // Reset input state if HDMI is plug out to + // avoid entering extended mode immediately after HDMI is plug in + mActiveInputState = true; + } + } +} + +void DisplayAnalyzer::handleBlankEvent(bool blank) +{ + mBlankDevice = blank; + // force geometry changed in the secondary device to reset layer composition type + for (int i = 0; i < (int)mCachedNumDisplays; i++) { + if (i == IDisplayDevice::DEVICE_PRIMARY) { + continue; + } + if (mCachedDisplays[i]) { + mCachedDisplays[i]->flags |= HWC_GEOMETRY_CHANGED; + } + } + blankSecondaryDevice(); +} + +void DisplayAnalyzer::handleTimingEvent() +{ + // check whether external device is connected, reset refresh rate to match video frame rate + // if video is in playing state or reset refresh rate to default preferred one if video is not + // at playing state + Hwcomposer *hwc = &Hwcomposer::getInstance(); + ExternalDevice *dev = NULL; + dev = (ExternalDevice *)hwc->getDisplayDevice(IDisplayDevice::DEVICE_EXTERNAL); + if (!dev) { + return; + } + + if (!dev->isConnected()) { + return; + } + + if (hwc->getMultiDisplayObserver()->isExternalDeviceTimingFixed()) { + VTRACE("Timing of external device is fixed."); + return; + } + + int hz = 0; + if (mVideoStateMap.size() == 1) { + VideoSourceInfo info; + int instanceID = mVideoStateMap.keyAt(0); + status_t err = hwc->getMultiDisplayObserver()->getVideoSourceInfo( + instanceID, &info); + if (err == NO_ERROR) { + hz = info.frameRate; + } + } + + dev->setRefreshRate(hz); +} + +void DisplayAnalyzer::handleVideoEvent(int instanceID, int state) +{ + mVideoStateMap.removeItem(instanceID); + if (state != VIDEO_PLAYBACK_STOPPED) { + mVideoStateMap.add(instanceID, state); + } + + Hwcomposer *hwc = &Hwcomposer::getInstance(); + + // sanity check + if (hwc->getMultiDisplayObserver()->getVideoSessionNumber() != + (int)mVideoStateMap.size()) { + WTRACE("session number does not match!!"); + mVideoStateMap.clear(); + if (state != VIDEO_PLAYBACK_STOPPED) { + mVideoStateMap.add(instanceID, state); + } + } + + // check if composition type needs to be reset + bool reset = false; + if ((state == VIDEO_PLAYBACK_STARTING) || + (state == VIDEO_PLAYBACK_STOPPING && mProtectedVideoSession)) { + // if video is in starting or stopping stage, overlay use is temporarily not allowed to + // avoid scrambed RGB overlay if video is protected. + mOverlayAllowed = false; + reset = true; + } else { + reset = !mOverlayAllowed; + mOverlayAllowed = true; + } + + if (reset) { + hwc_display_contents_1_t *content = NULL; + for (int i = 0; i < (int)mCachedNumDisplays; i++) { + setCompositionType(i, HWC_FRAMEBUFFER, true); + } + } + + if (mVideoStateMap.size() == 0) { + // reset active input state after video playback stops. + // MDS should update input state in 5 seconds after video playback starts + mActiveInputState = true; + } + + mProtectedVideoSession = false; + if (state == VIDEO_PLAYBACK_STARTED) { + VideoSourceInfo info; + hwc->getMultiDisplayObserver()->getVideoSourceInfo( + getFirstVideoInstanceSessionID(), &info); + mProtectedVideoSession = info.isProtected; + } + // Setting timing immediately, + // Don't posthone to next circle + handleTimingEvent(); + + handleVideoCheckEvent(); +} + +void DisplayAnalyzer::blankSecondaryDevice() +{ + hwc_display_contents_1_t *content = NULL; + hwc_layer_1 *layer = NULL; + for (int i = 0; i < (int)mCachedNumDisplays; i++) { + if (i == IDisplayDevice::DEVICE_PRIMARY) { + continue; + } + content = mCachedDisplays[i]; + if (content == NULL) { + continue; + } + + for (int j = 0; j < (int)content->numHwLayers - 1; j++) { + layer = &content->hwLayers[j]; + if (!layer) { + continue; + } + if (mBlankDevice) { + layer->hints |= HWC_HINT_CLEAR_FB; + layer->flags &= ~HWC_SKIP_LAYER; + layer->compositionType = HWC_OVERLAY; + } else { + layer->hints &= ~HWC_HINT_CLEAR_FB; + layer->compositionType = HWC_FRAMEBUFFER; + } + } + } +} + +void DisplayAnalyzer::handleInputEvent(bool active) +{ + if (active == mActiveInputState) { + WTRACE("same input state: %d", active); + } + mActiveInputState = active; + if (!mVideoExtModeEligible) { + ITRACE("not eligible for video extended mode"); + return; + } + + if (active) { + exitVideoExtMode(); + } else { + enterVideoExtMode(); + } +} + +void DisplayAnalyzer::handleDpmsEvent(int delayCount) +{ + if (mActiveInputState || !mVideoExtModeEligible) { + ITRACE("aborting display power off in video extended mode"); + return; + } + + if (delayCount < DELAY_BEFORE_DPMS_OFF) { + Event e; + e.type = DPMS_EVENT; + e.nValue = delayCount + 1; + postEvent(e); + Hwcomposer::getInstance().invalidate(); + return; + } + + if (Hwcomposer::getInstance().getVsyncManager()->getVsyncSource() == + IDisplayDevice::DEVICE_PRIMARY) { + Hwcomposer::getInstance().getDrm()->setDpmsMode( + IDisplayDevice::DEVICE_PRIMARY, + IDisplayDevice::DEVICE_DISPLAY_STANDBY); + ETRACE("primary display is source of vsync, we only dim backlight"); + return; + } + + // panel can't be powered off as touch panel shares the power supply with LCD. + DTRACE("primary display coupled with touch on Saltbay, only dim backlight"); + Hwcomposer::getInstance().getDrm()->setDpmsMode( + IDisplayDevice::DEVICE_PRIMARY, + IDisplayDevice::DEVICE_DISPLAY_STANDBY); + //IDisplayDevice::DEVICE_DISPLAY_OFF); + return; +} + + +void DisplayAnalyzer::handleIdleEntryEvent(int count) +{ + DTRACE("handling idle entry event, count %d", count); + if (hasProtectedLayer()) { + ITRACE("Ignoring idle entry as protected layer exists."); + setCompositionType(0, HWC_FRAMEBUFFER, true); + return; + } + + // stop idle entry if external device is connected + if (mCachedDisplays && mCachedDisplays[IDisplayDevice::DEVICE_EXTERNAL]) { + ITRACE("Ignoring idle entry as external device is connected."); + setCompositionType(0, HWC_FRAMEBUFFER, true); + return; + } + + // stop idle entry if video playback is active + // TODO: remove this check for Annidale + if (mVideoStateMap.size() > 0) { + ITRACE("Ignoring idle entry as video session is active."); + setCompositionType(0, HWC_FRAMEBUFFER, true); + return; + } + + setCompositionType(0, HWC_FORCE_FRAMEBUFFER, true); + + // next prepare/set will exit idle state. + Event e; + e.type = IDLE_EXIT_EVENT; + postEvent(e); +} + +void DisplayAnalyzer::handleIdleExitEvent() +{ + DTRACE("handling idle exit event"); + + setCompositionType(0, HWC_FRAMEBUFFER, true); +} + +void DisplayAnalyzer::handleVideoCheckEvent() +{ + // check if the first seen video layer on secondary device (HDMI/WFD) is marked as skipped + // it is assumed video is always skipped if the first seen video layer is skipped + // this is to workaround secure video layer transmitted over non secure output + // and HWC_SKIP_LAYER set during rotation animation. + mIgnoreVideoSkipFlag = false; + + if (mVideoStateMap.size() != 1 || + mCachedNumDisplays <= 1) { + return; + } + + intptr_t videoHandles[mCachedNumDisplays]; + for (int i = 0; i < (int)mCachedNumDisplays; i++) { + videoHandles[i] = 0; + hwc_display_contents_1_t *content = mCachedDisplays[i]; + if (content == NULL) { + continue; + } + for (int j = 0; j < (int)content->numHwLayers - 1; j++) { + if (isVideoLayer(content->hwLayers[j])) { + videoHandles[i] = (intptr_t)content->hwLayers[j].handle; + if (i > 0) { + mIgnoreVideoSkipFlag = !(content->hwLayers[j].flags & HWC_SKIP_LAYER); + ITRACE("Ignoring video HWC_SKIP_LAYER: %d on output %d", mIgnoreVideoSkipFlag, i); + return; + } + break; + } + } + } + + if (videoHandles[0]) { + WTRACE("Video is on the primary panel only"); + return; + } + + // video state map indicates video session is active and there is secondary + // display, need to continue checking as video is not found in the buffers yet + Event e; + e.type = VIDEO_CHECK_EVENT; + postEvent(e); +} + +void DisplayAnalyzer::enterVideoExtMode() +{ + if (mVideoExtModeActive) { + WTRACE("already in video extended mode."); + return; + } + + ITRACE("entering video extended mode..."); + mVideoExtModeActive = true; + Hwcomposer::getInstance().getVsyncManager()->resetVsyncSource(); + + setCompositionType(0, HWC_OVERLAY, true); + + // Do not power off primary display immediately as flip is asynchronous + Event e; + e.type = DPMS_EVENT; + e.nValue = 0; + postEvent(e); + Hwcomposer::getInstance().invalidate(); +} + +void DisplayAnalyzer::exitVideoExtMode() +{ + if (!mVideoExtModeActive) { + WTRACE("Not in video extended mode"); + return; + } + + ITRACE("exiting video extended mode..."); + + mVideoExtModeActive = false; + + Hwcomposer::getInstance().getDrm()->setDpmsMode( + IDisplayDevice::DEVICE_PRIMARY, + IDisplayDevice::DEVICE_DISPLAY_ON); + + Hwcomposer::getInstance().getVsyncManager()->resetVsyncSource(); + + setCompositionType(0, HWC_FRAMEBUFFER, true); +} + +bool DisplayAnalyzer::isPresentationLayer(hwc_layer_1_t &layer) +{ + if (layer.handle == NULL) { + return false; + } + if (mCachedDisplays == NULL) { + return false; + } + // check if the given layer exists in the primary device + hwc_display_contents_1_t *content = mCachedDisplays[0]; + if (content == NULL) { + return false; + } + for (size_t i = 0; i < content->numHwLayers - 1; i++) { + if (content->hwLayers[i].handle == layer.handle) { + VTRACE("Layer exists for Primary device"); + return false; + } + } + return true; +} + +bool DisplayAnalyzer::hasProtectedLayer() +{ + DataBuffer * buffer = NULL; + hwc_display_contents_1_t *content = NULL; + BufferManager *bm = Hwcomposer::getInstance().getBufferManager(); + + if (bm == NULL){ + return false; + } + + if (mCachedDisplays == NULL) { + return false; + } + // check if the given layer exists in the primary device + for (int index = 0; index < (int)mCachedNumDisplays; index++) { + content = mCachedDisplays[index]; + if (content == NULL) { + continue; + } + + for (size_t i = 0; i < content->numHwLayers - 1; i++) { + if (isProtectedLayer(content->hwLayers[i])) + return true; + } + } + + return false; +} + +bool DisplayAnalyzer::isProtectedLayer(hwc_layer_1_t &layer) +{ + if (!layer.handle) { + return false; + } + bool ret = false; + BufferManager *bm = Hwcomposer::getInstance().getBufferManager(); + DataBuffer *buffer = bm->lockDataBuffer(layer.handle); + if (!buffer) { + ETRACE("failed to get buffer"); + } else { + ret = GraphicBuffer::isProtectedBuffer((GraphicBuffer*)buffer); + bm->unlockDataBuffer(buffer); + } + return ret; +} + +bool DisplayAnalyzer::ignoreVideoSkipFlag() +{ + return mIgnoreVideoSkipFlag; +} + +void DisplayAnalyzer::setCompositionType(hwc_display_contents_1_t *display, int type) +{ + for (size_t i = 0; i < display->numHwLayers - 1; i++) { + hwc_layer_1_t *layer = &display->hwLayers[i]; + if (layer) layer->compositionType = type; + } +} + +void DisplayAnalyzer::setCompositionType(int device, int type, bool reset) +{ + hwc_display_contents_1_t *content = mCachedDisplays[device]; + if (content == NULL) { + ETRACE("Invalid device %d", device); + return; + } + + // don't need to set geometry changed if layers are just needed to be marked + if (reset) { + content->flags |= HWC_GEOMETRY_CHANGED; + } + + setCompositionType(content, type); +} + +int DisplayAnalyzer::getFirstVideoInstanceSessionID() { + if (mVideoStateMap.size() >= 1) { + return mVideoStateMap.keyAt(0); + } + return -1; +} + +} // namespace intel +} // namespace android + diff --git a/merrifield/common/base/DisplayAnalyzer.h b/merrifield/common/base/DisplayAnalyzer.h new file mode 100755 index 0000000..abe5d91 --- /dev/null +++ b/merrifield/common/base/DisplayAnalyzer.h @@ -0,0 +1,145 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef DISPLAY_ANALYZER_H +#define DISPLAY_ANALYZER_H + +#include <utils/threads.h> +#include <utils/Vector.h> + + +namespace android { +namespace intel { + + +class DisplayAnalyzer { +public: + DisplayAnalyzer(); + virtual ~DisplayAnalyzer(); + +public: + bool initialize(); + void deinitialize(); + void analyzeContents(size_t numDisplays, hwc_display_contents_1_t** displays); + bool isVideoExtModeActive(); + bool isVideoExtModeEnabled(); + bool isVideoLayer(hwc_layer_1_t &layer); + bool isVideoFullScreen(int device, hwc_layer_1_t &layer); + bool isOverlayAllowed(); + int getVideoInstances(); + void postHotplugEvent(bool connected); + void postVideoEvent(int instanceID, int state); + void postInputEvent(bool active); + void postVideoEvent(int instances, int instanceID, bool preparing, bool playing); + void postBlankEvent(bool blank); + void postIdleEntryEvent(); + bool isPresentationLayer(hwc_layer_1_t &layer); + bool isProtectedLayer(hwc_layer_1_t &layer); + bool ignoreVideoSkipFlag(); + int getFirstVideoInstanceSessionID(); + +private: + enum DisplayEventType { + HOTPLUG_EVENT, + BLANK_EVENT, + VIDEO_EVENT, + TIMING_EVENT, + INPUT_EVENT, + DPMS_EVENT, + IDLE_ENTRY_EVENT, + IDLE_EXIT_EVENT, + VIDEO_CHECK_EVENT, + }; + + struct Event { + int type; + + struct VideoEvent { + int instanceID; + int state; + }; + + union { + bool bValue; + int nValue; + VideoEvent videoEvent; + }; + }; + inline void postEvent(Event& e); + inline bool getEvent(Event& e); + void handlePendingEvents(); + void handleHotplugEvent(bool connected); + void handleBlankEvent(bool blank); + void handleVideoEvent(int instanceID, int state); + void handleTimingEvent(); + void handleInputEvent(bool active); + void handleDpmsEvent(int delayCount); + void handleIdleEntryEvent(int count); + void handleIdleExitEvent(); + void handleVideoCheckEvent(); + + void blankSecondaryDevice(); + void handleVideoExtMode(); + void checkVideoExtMode(); + void enterVideoExtMode(); + void exitVideoExtMode(); + bool hasProtectedLayer(); + inline void setCompositionType(hwc_display_contents_1_t *content, int type); + inline void setCompositionType(int device, int type, bool reset); + +private: + // Video playback state, must match defintion in Multi Display Service + enum + { + VIDEO_PLAYBACK_IDLE, + VIDEO_PLAYBACK_STARTING, + VIDEO_PLAYBACK_STARTED, + VIDEO_PLAYBACK_STOPPING, + VIDEO_PLAYBACK_STOPPED, + }; + + enum + { + // number of flips before display can be powered off in video extended mode + DELAY_BEFORE_DPMS_OFF = 0, + }; + +private: + bool mInitialized; + bool mVideoExtModeEnabled; + bool mVideoExtModeEligible; + bool mVideoExtModeActive; + bool mBlankDevice; + bool mOverlayAllowed; + bool mActiveInputState; + // workaround HWC_SKIP_LAYER set during rotation for extended video mode + // by default if layer has HWC_SKIP_LAYER flag it should not be processed by HWC + bool mIgnoreVideoSkipFlag; + bool mProtectedVideoSession; + // map video instance ID to video state + KeyedVector<int, int> mVideoStateMap; + int mCachedNumDisplays; + hwc_display_contents_1_t** mCachedDisplays; + Vector<Event> mPendingEvents; + Mutex mEventMutex; + Condition mEventHandledCondition; +}; + +} // namespace intel +} // namespace android + + + +#endif /* DISPLAY_ANALYZER_H */ diff --git a/merrifield/common/base/Drm.cpp b/merrifield/common/base/Drm.cpp new file mode 100644 index 0000000..619e4e5 --- /dev/null +++ b/merrifield/common/base/Drm.cpp @@ -0,0 +1,721 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <fcntl.h> +#include <errno.h> +#include <HwcTrace.h> +#include <IDisplayDevice.h> +#include <DrmConfig.h> +#include <Drm.h> +#include <Hwcomposer.h> + +namespace android { +namespace intel { + +Drm::Drm() + : mDrmFd(0), + mLock(), + mInitialized(false) +{ + memset(&mOutputs, 0, sizeof(mOutputs)); +} + +Drm::~Drm() +{ + WARN_IF_NOT_DEINIT(); +} + +bool Drm::initialize() +{ + if (mInitialized) { + WTRACE("Drm object has been initialized"); + return true; + } + + const char *path = DrmConfig::getDrmPath(); + mDrmFd = open(path, O_RDWR, 0); + if (mDrmFd < 0) { + ETRACE("failed to open Drm, error: %s", strerror(errno)); + return false; + } + DTRACE("mDrmFd = %d", mDrmFd); + + memset(&mOutputs, 0, sizeof(mOutputs)); + mInitialized = true; + return true; +} + +void Drm::deinitialize() +{ + for (int i = 0; i < OUTPUT_MAX; i++) { + resetOutput(i); + } + + if (mDrmFd) { + close(mDrmFd); + mDrmFd = 0; + } + mInitialized = false; +} + +bool Drm::detect(int device) +{ + RETURN_FALSE_IF_NOT_INIT(); + + Mutex::Autolock _l(mLock); + int outputIndex = getOutputIndex(device); + if (outputIndex < 0 ) { + return false; + } + + resetOutput(outputIndex); + + // get drm resources + drmModeResPtr resources = drmModeGetResources(mDrmFd); + if (!resources) { + ETRACE("fail to get drm resources, error: %s", strerror(errno)); + return false; + } + + drmModeConnectorPtr connector = NULL; + DrmOutput *output = &mOutputs[outputIndex]; + bool ret = false; + + // find connector for the given device + for (int i = 0; i < resources->count_connectors; i++) { + if (!resources->connectors || !resources->connectors[i]) { + ETRACE("fail to get drm resources connectors, error: %s", strerror(errno)); + continue; + } + + connector = drmModeGetConnector(mDrmFd, resources->connectors[i]); + if (!connector) { + ETRACE("drmModeGetConnector failed"); + continue; + } + + if (connector->connector_type != DrmConfig::getDrmConnector(device)) { + drmModeFreeConnector(connector); + continue; + } + + if (connector->connection != DRM_MODE_CONNECTED) { + ITRACE("device %d is not connected", device); + drmModeFreeConnector(connector); + ret = true; + break; + } + + output->connector = connector; + output->connected = true; + + // get proper encoder for the given connector + if (connector->encoder_id) { + ITRACE("Drm connector has encoder attached on device %d", device); + output->encoder = drmModeGetEncoder(mDrmFd, connector->encoder_id); + if (!output->encoder) { + ETRACE("failed to get encoder from a known encoder id"); + // fall through to get an encoder + } + } + if (!output->encoder) { + ITRACE("getting encoder for device %d", device); + drmModeEncoderPtr encoder; + for (int j = 0; j < resources->count_encoders; j++) { + if (!resources->encoders || !resources->encoders[j]) { + ETRACE("fail to get drm resources encoders, error: %s", strerror(errno)); + continue; + } + + encoder = drmModeGetEncoder(mDrmFd, resources->encoders[i]); + if (!encoder) { + ETRACE("drmModeGetEncoder failed"); + continue; + } + if (encoder->encoder_type == DrmConfig::getDrmEncoder(device)) { + output->encoder = encoder; + break; + } + drmModeFreeEncoder(encoder); + encoder = NULL; + } + } + if (!output->encoder) { + ETRACE("failed to get drm encoder"); + break; + } + + // get an attached crtc or spare crtc + if (output->encoder->crtc_id) { + ITRACE("Drm encoder has crtc attached on device %d", device); + output->crtc = drmModeGetCrtc(mDrmFd, output->encoder->crtc_id); + if (!output->crtc) { + ETRACE("failed to get crtc from a known crtc id"); + // fall through to get a spare crtc + } + } + if (!output->crtc) { + ITRACE("getting crtc for device %d", device); + drmModeCrtcPtr crtc; + for (int j = 0; j < resources->count_crtcs; j++) { + if (!resources->crtcs || !resources->crtcs[j]) { + ETRACE("fail to get drm resources crtcs, error: %s", strerror(errno)); + continue; + } + + crtc = drmModeGetCrtc(mDrmFd, resources->crtcs[j]); + if (!crtc) { + ETRACE("drmModeGetCrtc failed"); + continue; + } + if (crtc->buffer_id == 0) { + output->crtc = crtc; + break; + } + drmModeFreeCrtc(crtc); + } + } + if (!output->crtc) { + ETRACE("failed to get drm crtc"); + break; + } + + // current mode + if (output->crtc->mode_valid) { + ITRACE("mode is valid, kernel mode settings"); + memcpy(&output->mode, &output->crtc->mode, sizeof(drmModeModeInfo)); + ret = true; + } else { + ITRACE("mode is invalid, setting preferred mode"); + ret = initDrmMode(outputIndex); + } + + if (outputIndex == OUTPUT_PRIMARY) { + if (!readIoctl(DRM_PSB_PANEL_ORIENTATION, &output->panelOrientation, sizeof(int))) { + ETRACE("failed to get device %d orientation", device); + output->panelOrientation = PANEL_ORIENTATION_0; + } + } else { + output->panelOrientation = PANEL_ORIENTATION_0; + } + break; + } + + if (!ret) { + if (output->connector == NULL && outputIndex != OUTPUT_PRIMARY) { + // a fatal failure on primary device + // non fatal on secondary device + WTRACE("device %d is disabled?", device); + ret = true; + } + resetOutput(outputIndex); + } else if (output->connected) { + ITRACE("mode is: %dx%d@%dHz", output->mode.hdisplay, output->mode.vdisplay, output->mode.vrefresh); + } + + drmModeFreeResources(resources); + return ret; +} + +bool Drm::isSameDrmMode(drmModeModeInfoPtr value, + drmModeModeInfoPtr base) const +{ + if (base->hdisplay == value->hdisplay && + base->vdisplay == value->vdisplay && + base->vrefresh == value->vrefresh && + (base->flags & value->flags) == value->flags) { + VTRACE("Drm mode is not changed"); + return true; + } + + return false; +} + +bool Drm::setDrmMode(int device, drmModeModeInfo& value) +{ + RETURN_FALSE_IF_NOT_INIT(); + Mutex::Autolock _l(mLock); + + if (device != IDisplayDevice::DEVICE_EXTERNAL) { + WTRACE("Setting mode on invalid device %d", device); + return false; + } + + int outputIndex = getOutputIndex(device); + if (outputIndex < 0 ) { + ETRACE("invalid device"); + return false; + } + + DrmOutput *output= &mOutputs[outputIndex]; + if (!output->connected) { + ETRACE("device is not connected"); + return false; + } + + if (output->connector->count_modes <= 0) { + ETRACE("invalid count of modes"); + return false; + } + + drmModeModeInfoPtr mode; + int index = 0; + for (int i = 0; i < output->connector->count_modes; i++) { + mode = &output->connector->modes[i]; + if (mode->type & DRM_MODE_TYPE_PREFERRED) { + index = i; + } + if (isSameDrmMode(&value, mode)) { + index = i; + break; + } + } + + mode = &output->connector->modes[index]; + return setDrmMode(outputIndex, mode); +} + +bool Drm::setRefreshRate(int device, int hz) +{ + RETURN_FALSE_IF_NOT_INIT(); + Mutex::Autolock _l(mLock); + + if (device != IDisplayDevice::DEVICE_EXTERNAL) { + WTRACE("Setting mode on invalid device %d", device); + return false; + } + + int outputIndex = getOutputIndex(device); + if (outputIndex < 0 ) { + ETRACE("invalid device"); + return false; + } + + DrmOutput *output= &mOutputs[outputIndex]; + if (!output->connected) { + ETRACE("device is not connected"); + return false; + } + + if (output->connector->count_modes <= 0) { + ETRACE("invalid count of modes"); + return false; + } + + drmModeModeInfoPtr mode; + int index = 0; + for (int i = 0; i < output->connector->count_modes; i++) { + mode = &output->connector->modes[i]; + if (mode->type & DRM_MODE_TYPE_PREFERRED) { + index = i; + } + if (mode->hdisplay == output->mode.hdisplay && + mode->vdisplay == output->mode.vdisplay && + mode->vrefresh == (uint32_t)hz) { + index = i; + break; + } + } + + mode = &output->connector->modes[index]; + return setDrmMode(outputIndex, mode); +} + +bool Drm::writeReadIoctl(unsigned long cmd, void *data, + unsigned long size) +{ + int err; + + if (mDrmFd <= 0) { + ETRACE("drm is not initialized"); + return false; + } + + if (!data || !size) { + ETRACE("invalid parameters"); + return false; + } + + err = drmCommandWriteRead(mDrmFd, cmd, data, size); + if (err) { + WTRACE("failed to call %ld ioctl with failure %d", cmd, err); + return false; + } + + return true; +} + +bool Drm::writeIoctl(unsigned long cmd, void *data, + unsigned long size) +{ + int err; + + if (mDrmFd <= 0) { + ETRACE("drm is not initialized"); + return false; + } + + if (!data || !size) { + ETRACE("invalid parameters"); + return false; + } + + err = drmCommandWrite(mDrmFd, cmd, data, size); + if (err) { + WTRACE("failed to call %ld ioctl with failure %d", cmd, err); + return false; + } + + return true; +} + + +bool Drm::readIoctl(unsigned long cmd, void *data, + unsigned long size) +{ + int err; + + if (mDrmFd <= 0) { + ETRACE("drm is not initialized"); + return false; + } + + if (!data || !size) { + ETRACE("invalid parameters"); + return false; + } + + err = drmCommandRead(mDrmFd, cmd, data, size); + if (err) { + WTRACE("failed to call %ld ioctl with failure %d", cmd, err); + return false; + } + + return true; +} + + +int Drm::getDrmFd() const +{ + return mDrmFd; +} + +bool Drm::getModeInfo(int device, drmModeModeInfo& mode) +{ + Mutex::Autolock _l(mLock); + + int outputIndex = getOutputIndex(device); + if (outputIndex < 0 ) { + return false; + } + + DrmOutput *output= &mOutputs[outputIndex]; + if (output->connected == false) { + ETRACE("device is not connected"); + return false; + } + + if (output->mode.hdisplay == 0 || output->mode.vdisplay == 0) { + ETRACE("invalid width or height"); + return false; + } + + memcpy(&mode, &output->mode, sizeof(drmModeModeInfo)); + return true; +} + +bool Drm::getPhysicalSize(int device, uint32_t& width, uint32_t& height) +{ + Mutex::Autolock _l(mLock); + + int outputIndex = getOutputIndex(device); + if (outputIndex < 0 ) { + return false; + } + + DrmOutput *output= &mOutputs[outputIndex]; + if (output->connected == false) { + ETRACE("device is not connected"); + return false; + } + + width = output->connector->mmWidth; + height = output->connector->mmHeight; + return true; +} + +bool Drm::isConnected(int device) +{ + Mutex::Autolock _l(mLock); + + int output = getOutputIndex(device); + if (output < 0 ) { + return false; + } + + return mOutputs[output].connected; +} + +bool Drm::setDpmsMode(int device, int mode) +{ + Mutex::Autolock _l(mLock); + + int output = getOutputIndex(device); + if (output < 0 ) { + return false; + } + + if (mode != IDisplayDevice::DEVICE_DISPLAY_OFF && + mode != IDisplayDevice::DEVICE_DISPLAY_STANDBY && + mode != IDisplayDevice::DEVICE_DISPLAY_ON) { + ETRACE("invalid mode %d", mode); + return false; + } + + DrmOutput *out = &mOutputs[output]; + if (!out->connected) { + ETRACE("device is not connected"); + return false; + } + + drmModePropertyPtr props; + for (int i = 0; i < out->connector->count_props; i++) { + props = drmModeGetProperty(mDrmFd, out->connector->props[i]); + if (!props) { + continue; + } + + if (strcmp(props->name, "DPMS") == 0) { + int ret = drmModeConnectorSetProperty( + mDrmFd, + out->connector->connector_id, + props->prop_id, + (mode == IDisplayDevice::DEVICE_DISPLAY_ON) ? DRM_MODE_DPMS_ON : + IDisplayDevice::DEVICE_DISPLAY_STANDBY == mode ? + DRM_MODE_DPMS_STANDBY : DRM_MODE_DPMS_OFF); + drmModeFreeProperty(props); + if (ret != 0) { + ETRACE("unable to set DPMS %d", mode); + return false; + } else { + return true; + } + } + drmModeFreeProperty(props); + } + return false; +} + +void Drm::resetOutput(int index) +{ + DrmOutput *output = &mOutputs[index]; + + output->connected = false; + memset(&output->mode, 0, sizeof(drmModeModeInfo)); + + if (output->connector) { + drmModeFreeConnector(output->connector); + output->connector = 0; + } + if (output->encoder) { + drmModeFreeEncoder(output->encoder); + output->encoder = 0; + } + if (output->crtc) { + drmModeFreeCrtc(output->crtc); + output->crtc = 0; + } + if (output->fbId) { + drmModeRmFB(mDrmFd, output->fbId); + output->fbId = 0; + } + if (output->fbHandle) { + Hwcomposer::getInstance().getBufferManager()->freeFrameBuffer( + (buffer_handle_t)output->fbHandle); + output->fbHandle = 0; + } +} + +bool Drm::initDrmMode(int outputIndex) +{ + DrmOutput *output= &mOutputs[outputIndex]; + if (output->connector->count_modes <= 0) { + ETRACE("invalid count of modes"); + return false; + } + + drmModeModeInfoPtr mode; + int index = 0; + for (int i = 0; i < output->connector->count_modes; i++) { + mode = &output->connector->modes[i]; + if (mode->type & DRM_MODE_TYPE_PREFERRED) { + index = i; + break; + } + } + + return setDrmMode(outputIndex, &output->connector->modes[index]); +} + +bool Drm::setDrmMode(int index, drmModeModeInfoPtr mode) +{ + DrmOutput *output = &mOutputs[index]; + + int oldFbId =0; + buffer_handle_t oldFbHandle = 0; + + drmModeModeInfo currentMode; + memcpy(¤tMode, &output->mode, sizeof(drmModeModeInfo)); + + if (isSameDrmMode(mode, ¤tMode)) + return true; + + + if (output->fbId) { + oldFbId = output->fbId; + output->fbId = 0; + } + + if (output->fbHandle) { + oldFbHandle = output->fbHandle; + output->fbHandle = 0; + } + + // allocate frame buffer + int stride = 0; + output->fbHandle = Hwcomposer::getInstance().getBufferManager()->allocFrameBuffer( + mode->hdisplay, mode->vdisplay, &stride); + if (output->fbHandle == 0) { + ETRACE("failed to allocate frame buffer"); + return false; + } + + uint32_t bo_handles[4] = {0}; + uint32_t pitches[4] = {0}; + uint32_t offsets[4] = {0}; + int ret = 0; + + // We use bo_handles[0] and bo_handles[1] to store buffer_handle_t + // to support 32 and 64 platforms. + bo_handles[0] = ((unsigned long)(output->fbHandle)) & 0xffffffff; + bo_handles[1] = ((unsigned long)(output->fbHandle) >> 32) & 0xffffffff; + pitches[0] = stride * DrmConfig::getFrameBufferBpp() / 8; + + ret = drmModeAddFB2( + mDrmFd, + mode->hdisplay, + mode->vdisplay, + DrmConfig::convertHalFormatToDrmFormat(DrmConfig::getFrameBufferFormat()), + bo_handles, + pitches, + offsets, + &output->fbId, + 0); + if (ret != 0) { + ETRACE("drmModeAddFB2 failed, error: %d", ret); + return false; + } + + ITRACE("mode set: %dx%d@%dHz", mode->hdisplay, mode->vdisplay, mode->vrefresh); + + ret = drmModeSetCrtc(mDrmFd, output->crtc->crtc_id, output->fbId, 0, 0, + &output->connector->connector_id, 1, mode); + if (ret == 0) { + //save mode + memcpy(&output->mode, mode, sizeof(drmModeModeInfo)); + } else { + ETRACE("drmModeSetCrtc failed. error: %d", ret); + } + + if (oldFbId) { + drmModeRmFB(mDrmFd, oldFbId); + } + + if (oldFbHandle) { + Hwcomposer::getInstance().getBufferManager()->freeFrameBuffer((buffer_handle_t)oldFbHandle); + } + + return ret == 0; +} + +int Drm::getOutputIndex(int device) +{ + switch (device) { + case IDisplayDevice::DEVICE_PRIMARY: + return OUTPUT_PRIMARY; + case IDisplayDevice::DEVICE_EXTERNAL: + return OUTPUT_EXTERNAL; + default: + ETRACE("invalid display device"); + break; + } + + return -1; +} + +int Drm::getPanelOrientation(int device) +{ + int outputIndex = getOutputIndex(device); + if (outputIndex < 0) { + ETRACE("invalid device"); + return PANEL_ORIENTATION_0; + } + + DrmOutput *output= &mOutputs[outputIndex]; + if (output->connected == false) { + ETRACE("device is not connected"); + return PANEL_ORIENTATION_0; + } + + return output->panelOrientation; +} + +// HWC 1.4 requires that we return all of the compatible configs in getDisplayConfigs +// this is needed so getActiveConfig/setActiveConfig work correctly. It is up to the +// user space to decide what speed to send. +drmModeModeInfoPtr Drm::detectAllConfigs(int device, int *modeCount) +{ + RETURN_NULL_IF_NOT_INIT(); + Mutex::Autolock _l(mLock); + + if (modeCount != NULL) + *modeCount = 0; + else + return NULL; + + int outputIndex = getOutputIndex(device); + if (outputIndex < 0) { + ETRACE("invalid device"); + return NULL; + } + + DrmOutput *output= &mOutputs[outputIndex]; + if (!output->connected) { + ETRACE("device is not connected"); + return NULL; + } + + if (output->connector->count_modes <= 0) { + ETRACE("invalid count of modes"); + return NULL; + } + + *modeCount = output->connector->count_modes; + return output->connector->modes; +} + +} // namespace intel +} // namespace android + diff --git a/merrifield/common/base/Drm.h b/merrifield/common/base/Drm.h new file mode 100644 index 0000000..4db0bc6 --- /dev/null +++ b/merrifield/common/base/Drm.h @@ -0,0 +1,101 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef __DRM_H__ +#define __DRM_H__ + +#include <utils/Mutex.h> +#include <hardware/hwcomposer.h> + +// TODO: psb_drm.h is IP specific defintion +#include <linux/psb_drm.h> + +extern "C" { +#include "xf86drm.h" +#include "xf86drmMode.h" +} + +namespace android { +namespace intel { + +enum { + PANEL_ORIENTATION_0 = 0, + PANEL_ORIENTATION_180 +}; + +class Drm { +public: + Drm(); + virtual ~Drm(); +public: + bool initialize(); + void deinitialize(); + bool detect(int device); + bool setDrmMode(int device, drmModeModeInfo& value); + bool setRefreshRate(int device, int hz); + bool writeReadIoctl(unsigned long cmd, void *data, + unsigned long size); + bool writeIoctl(unsigned long cmd, void *data, + unsigned long size); + bool readIoctl(unsigned long cmd, void *data, + unsigned long size); + + bool isConnected(int device); + bool setDpmsMode(int device, int mode); + int getDrmFd() const; + bool getModeInfo(int device, drmModeModeInfo& mode); + bool getPhysicalSize(int device, uint32_t& width, uint32_t& height); + bool isSameDrmMode(drmModeModeInfoPtr mode, drmModeModeInfoPtr base) const; + int getPanelOrientation(int device); + drmModeModeInfoPtr detectAllConfigs(int device, int *modeCount); + +private: + bool initDrmMode(int index); + bool setDrmMode(int index, drmModeModeInfoPtr mode); + void resetOutput(int index); + + // map device type to output index, return -1 if not mapped + inline int getOutputIndex(int device); + +private: + // DRM object index + enum { + OUTPUT_PRIMARY = 0, + OUTPUT_EXTERNAL, + OUTPUT_MAX, + }; + + struct DrmOutput { + drmModeConnectorPtr connector; + drmModeEncoderPtr encoder; + drmModeCrtcPtr crtc; + drmModeModeInfo mode; + buffer_handle_t fbHandle; + uint32_t fbId; + int connected; + int panelOrientation; + } mOutputs[OUTPUT_MAX]; + + int mDrmFd; + Mutex mLock; + bool mInitialized; +}; + +} // namespace intel +} // namespace android + + + +#endif /* __DRM_H__ */ diff --git a/merrifield/common/base/HwcLayer.cpp b/merrifield/common/base/HwcLayer.cpp new file mode 100755 index 0000000..04fd1bb --- /dev/null +++ b/merrifield/common/base/HwcLayer.cpp @@ -0,0 +1,409 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <HwcTrace.h> +#include <Drm.h> +#include <HwcLayer.h> +#include <Hwcomposer.h> +#include <GraphicBuffer.h> +#include <IDisplayDevice.h> +#include <DisplayQuery.h> +#include <PlaneCapabilities.h> +#include <cutils/properties.h> + + +namespace android { +namespace intel { + +inline bool operator==(const hwc_rect_t& x, const hwc_rect_t& y) +{ + return (x.top == y.top && + x.bottom == y.bottom && + x.left == y.left && + x.right == y.right); +} + +inline bool operator !=(const hwc_rect_t& x, const hwc_rect_t& y) +{ + return !operator==(x, y); +} + +inline bool operator ==(const hwc_frect_t& x, const hwc_frect_t& y) +{ + return (x.top == y.top && + x.bottom == y.bottom && + x.left == y.left && + x.right == y.right); +} + +inline bool operator !=(const hwc_frect_t& x, const hwc_frect_t& y) +{ + return !operator==(x, y); +} + +HwcLayer::HwcLayer(int index, hwc_layer_1_t *layer) + : mIndex(index), + mZOrder(index + 1), // 0 is reserved for frame buffer target + mDevice(0), + mLayer(layer), + mPlane(0), + mFormat(DataBuffer::FORMAT_INVALID), + mWidth(0), + mHeight(0), + mUsage(0), + mHandle(0), + mIsProtected(false), + mType(LAYER_FB), + mPriority(0), + mTransform(0), + mStaticCount(0), + mUpdated(false) +{ + memset(&mSourceCropf, 0, sizeof(mSourceCropf)); + memset(&mDisplayFrame, 0, sizeof(mDisplayFrame)); + memset(&mStride, 0, sizeof(mStride)); + + mPlaneCandidate = false; + setupAttributes(); + +#ifdef HWC_TRACE_FPS + mTraceFps = false; + char prop[PROPERTY_VALUE_MAX]; + if (property_get("debug.hwc.fps_trace.enable", prop, "0") > 0) { + mTraceFps = atoi(prop); + } + mLastHandle = NULL; + + if (mTraceFps) { + // holding up to 6 seconds of samples at 60Hz + mFrames.setCapacity(6 * 60); + } +#endif +} + +HwcLayer::~HwcLayer() +{ + if (mPlane) { + WTRACE("HwcLayer is not cleaned up"); + } + + mLayer = NULL; + mPlane = NULL; + +#ifdef HWC_TRACE_FPS + mFrames.clear(); +#endif +} + +bool HwcLayer::attachPlane(DisplayPlane* plane, int device) +{ + if (mPlane) { + ETRACE("failed to attach plane, plane exists"); + return false; + } + + if (!plane) { + ETRACE("Invalid plane"); + return false; + } + + mDevice = device; + //plane->setZOrder(mIndex); + plane->assignToDevice(device); + mPlane = plane; + return true; +} + +DisplayPlane* HwcLayer::detachPlane() +{ + // reset plane's z order + if (mPlane) + mPlane->setZOrder(-1); + DisplayPlane *plane = mPlane; + mPlane = 0; + mDevice = 0; + return plane; +} + +void HwcLayer::setType(uint32_t type) +{ + if (!mLayer) + return; + + switch (type) { + case LAYER_OVERLAY: + case LAYER_SKIPPED: + mLayer->compositionType = HWC_OVERLAY; + mLayer->hints |= HWC_HINT_CLEAR_FB; + break; + // NOTE: set compositionType to HWC_FRAMEBUFFER here so that we have + // a chance to submit the primary changes to HW. + // Upper layer HWComposer will reset the compositionType automatically. + case LAYER_FB: + case LAYER_FORCE_FB: + mLayer->compositionType = HWC_FRAMEBUFFER; + break; + case LAYER_SIDEBAND: + mLayer->compositionType = HWC_SIDEBAND; + break; + case LAYER_CURSOR_OVERLAY: + mLayer->compositionType = HWC_CURSOR_OVERLAY; + break; + default: + break; + } + + mType = type; +} + +uint32_t HwcLayer::getType() const +{ + return mType; +} + +void HwcLayer::setCompositionType(int32_t type) +{ + mLayer->compositionType = type; +} + +int32_t HwcLayer::getCompositionType() const +{ + return mLayer->compositionType; +} + +int HwcLayer::getIndex() const +{ + return mIndex; +} + +int HwcLayer::getZOrder() const +{ + return mZOrder; +} + +uint32_t HwcLayer::getFormat() const +{ + return mFormat; +} + +uint32_t HwcLayer::getBufferWidth() const +{ + return mWidth; +} + +uint32_t HwcLayer::getBufferHeight() const +{ + return mHeight; +} + +const stride_t& HwcLayer::getBufferStride() const +{ + return mStride; +} + +uint32_t HwcLayer::getUsage() const +{ + return mUsage; +} + +buffer_handle_t HwcLayer::getHandle() const +{ + return mHandle; +} + +uint32_t HwcLayer::getTransform() const +{ + return mTransform; +} + +bool HwcLayer::isProtected() const +{ + return mIsProtected; +} + +hwc_layer_1_t* HwcLayer::getLayer() const +{ + return mLayer; +} + +DisplayPlane* HwcLayer::getPlane() const +{ + return mPlane; +} + +void HwcLayer::setPriority(uint32_t priority) +{ + mPriority = priority; +} + +uint32_t HwcLayer::getPriority() const +{ + return mPriority; +} + +bool HwcLayer::update(hwc_layer_1_t *layer) +{ + // update layer + mLayer = layer; + setupAttributes(); + +#ifdef HWC_TRACE_FPS + if (mTraceFps && mLayer && mLayer->compositionType != HWC_FRAMEBUFFER_TARGET ) { + // 1 second = 1000000000 nano seconds + uint64_t now = systemTime(CLOCK_MONOTONIC); + if (mLastHandle != mHandle) { + mLastHandle = mHandle; + mFrames.push(now); + } + // calculate fps in 5-second time window + int frames = mFrames.size(); + while (frames && now - mFrames[0] > 5000000000LL) { + mFrames.removeItemsAt(0); + frames--; + } + double fps = 0; + if (frames > 1) { + fps = frames * 1000000000.0/ (now - mFrames[0]); + } + ITRACE("fps of layer %d is %.1f", mIndex, fps); + } +#endif + + // if not a FB layer & a plane was attached update plane's data buffer + if (mPlane) { + mPlane->setPosition(layer->displayFrame.left, + layer->displayFrame.top, + layer->displayFrame.right - layer->displayFrame.left, + layer->displayFrame.bottom - layer->displayFrame.top); + mPlane->setSourceCrop(layer->sourceCropf.left, + layer->sourceCropf.top, + layer->sourceCropf.right - layer->sourceCropf.left, + layer->sourceCropf.bottom - layer->sourceCropf.top); + mPlane->setTransform(layer->transform); + mPlane->setPlaneAlpha(layer->planeAlpha, layer->blending); + bool ret = mPlane->setDataBuffer(layer->handle); + if (ret == true) { + return true; + } + DTRACE("failed to set data buffer, reset handle to 0!!"); + mHandle = 0; + if (!mIsProtected) { + // typical case: rotated buffer is not ready or handle is null + return false; + } else { + // protected video has to be rendered using overlay. + // if buffer is not ready overlay will still be attached to this layer + // but rendering needs to be skipped. + WTRACE("ignoring result of data buffer setting for protected video"); + return true; + } + } + + return true; +} + +bool HwcLayer::isUpdated() +{ + return mUpdated; +} + +uint32_t HwcLayer::getStaticCount() +{ + return mStaticCount; +} + +void HwcLayer::postFlip() +{ + mUpdated = false; + if (mPlane) { + mPlane->postFlip(); + + // flip frame buffer target once in video extended mode to refresh screen, + // then mark type as LAYER_SKIPPED so it will not be flipped again. + // by doing this pipe for primary device can enter idle state + if (mDevice == IDisplayDevice::DEVICE_PRIMARY && + mType == LAYER_FRAMEBUFFER_TARGET && + Hwcomposer::getInstance().getDisplayAnalyzer()->isVideoExtModeActive()) { + DTRACE("Skipping frame buffer target..."); + mType = LAYER_SKIPPED; + } + } +} + +void HwcLayer::setupAttributes() +{ + if ((mLayer->flags & HWC_SKIP_LAYER) || + mTransform != mLayer->transform || + mSourceCropf != mLayer->sourceCropf || + mDisplayFrame != mLayer->displayFrame || + mHandle != mLayer->handle || + DisplayQuery::isVideoFormat(mFormat)) { + // TODO: same handle does not mean there is always no update + mUpdated = true; + mStaticCount = 0; + } else { + // protect it from exceeding its max + if (++mStaticCount > 1000) + mStaticCount = LAYER_STATIC_THRESHOLD + 1; + } + + // update handle always as it can become "NULL" + // if the given layer is not ready + mTransform = mLayer->transform; + mSourceCropf = mLayer->sourceCropf; + mDisplayFrame = mLayer->displayFrame; + mHandle = mLayer->handle; + + if (mFormat != DataBuffer::FORMAT_INVALID) { + // other attributes have been set. + return; + } + + if (mLayer->handle == NULL) { + VTRACE("invalid handle"); + return; + } + + BufferManager *bm = Hwcomposer::getInstance().getBufferManager(); + if (bm == NULL) { + // TODO: this check is redundant + return; + } + + DataBuffer *buffer = bm->lockDataBuffer(mLayer->handle); + if (!buffer) { + ETRACE("failed to get buffer"); + } else { + mFormat = buffer->getFormat(); + mWidth = buffer->getWidth(); + mHeight = buffer->getHeight(); + mStride = buffer->getStride(); + mPriority = (mSourceCropf.right - mSourceCropf.left) * (mSourceCropf.bottom - mSourceCropf.top); + mPriority <<= LAYER_PRIORITY_SIZE_OFFSET; + mPriority |= mIndex; + GraphicBuffer *gBuffer = (GraphicBuffer*)buffer; + mUsage = gBuffer->getUsage(); + mIsProtected = GraphicBuffer::isProtectedBuffer((GraphicBuffer*)buffer); + if (mIsProtected) { + mPriority |= LAYER_PRIORITY_PROTECTED; + } else if (PlaneCapabilities::isFormatSupported(DisplayPlane::PLANE_OVERLAY, this)) { + mPriority |= LAYER_PRIORITY_OVERLAY; + } + bm->unlockDataBuffer(buffer); + } +} + +} // namespace intel +} // namespace android diff --git a/merrifield/common/base/HwcLayer.h b/merrifield/common/base/HwcLayer.h new file mode 100755 index 0000000..07b91e6 --- /dev/null +++ b/merrifield/common/base/HwcLayer.h @@ -0,0 +1,137 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef HWC_LAYER_H +#define HWC_LAYER_H + +#include <hardware/hwcomposer.h> +#include <DisplayPlane.h> +#include <utils/Vector.h> + +//#define HWC_TRACE_FPS + +namespace android { +namespace intel { + +enum { + LAYER_STATIC_THRESHOLD = 10, +}; + +class HwcLayer { +public: + enum { + // LAYER_FB layers are marked as HWC_FRAMEBUFFER. + // And a LAYER_FB can become HWC_OVERLAY layers during + // revisiting layer list. + LAYER_FB = 0, + // LAYER_FORCE_FB layers are marked as HWC_FRAMEBUFFER. + // And a LAYER_FORCE_FB can never become HWC_OVERLAY layers during + // revisiting layer list. + LAYER_FORCE_FB, + // LAYER_OVERLAY layers are marked as HWC_OVERLAY + LAYER_OVERLAY, + // LAYER_SKIPPED layers are marked as HWC_OVERLAY with no plane attached + LAYER_SKIPPED, + // LAYER_FRAMEBUFFER_TARGET layers are marked as HWC_FRAMEBUFFER_TARGET + LAYER_FRAMEBUFFER_TARGET, + // LAYER_SIDEBAND layers have alternate path bypassing HWC after setup + LAYER_SIDEBAND, + // LAYER_CURSOR_OVERLAY layers support hardware cursor planes + LAYER_CURSOR_OVERLAY, + }; + + enum { + LAYER_PRIORITY_OVERLAY = 0x60000000UL, + LAYER_PRIORITY_PROTECTED = 0x70000000UL, + LAYER_PRIORITY_SIZE_OFFSET = 4, + }; +public: + HwcLayer(int index, hwc_layer_1_t *layer); + virtual ~HwcLayer(); + + // plane operations + bool attachPlane(DisplayPlane *plane, int device); + DisplayPlane* detachPlane(); + + void setType(uint32_t type); + uint32_t getType() const; + int32_t getCompositionType() const; + void setCompositionType(int32_t type); + + int getIndex() const; + int getZOrder() const; + uint32_t getFormat() const; + uint32_t getBufferWidth() const; + uint32_t getBufferHeight() const; + const stride_t& getBufferStride() const; + uint32_t getUsage() const; + buffer_handle_t getHandle() const; + uint32_t getTransform() const; + bool isProtected() const; + hwc_layer_1_t* getLayer() const; + DisplayPlane* getPlane() const; + + void setPriority(uint32_t priority); + uint32_t getPriority() const; + + bool update(hwc_layer_1_t *layer); + void postFlip(); + bool isUpdated(); + uint32_t getStaticCount(); + +public: + // temporary solution for plane assignment + bool mPlaneCandidate; + +private: + void setupAttributes(); + +private: + const int mIndex; + int mZOrder; + int mDevice; + hwc_layer_1_t *mLayer; + DisplayPlane *mPlane; + uint32_t mFormat; + uint32_t mWidth; + uint32_t mHeight; + stride_t mStride; + uint32_t mUsage; + buffer_handle_t mHandle; + bool mIsProtected; + uint32_t mType; + uint32_t mPriority; + uint32_t mTransform; + + // for smart composition + hwc_frect_t mSourceCropf; + hwc_rect_t mDisplayFrame; + uint32_t mStaticCount; + bool mUpdated; + +#ifdef HWC_TRACE_FPS + // for frame per second trace + bool mTraceFps; + buffer_handle_t mLastHandle; + Vector<uint64_t> mFrames; +#endif +}; + + +} // namespace intel +} // namespace android + + +#endif /* HWC_LAYER_H */ diff --git a/merrifield/common/base/HwcLayerList.cpp b/merrifield/common/base/HwcLayerList.cpp new file mode 100644 index 0000000..8df6ac9 --- /dev/null +++ b/merrifield/common/base/HwcLayerList.cpp @@ -0,0 +1,1096 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <HwcTrace.h> +#include <Drm.h> +#include <HwcLayerList.h> +#include <Hwcomposer.h> +#include <GraphicBuffer.h> +#include <IDisplayDevice.h> +#include <PlaneCapabilities.h> +#include <DisplayQuery.h> + +namespace android { +namespace intel { + +HwcLayerList::HwcLayerList(hwc_display_contents_1_t *list, int disp) + : mList(list), + mLayerCount(0), + mLayers(), + mFBLayers(), + mStaticLayersIndex(), + mSpriteCandidates(), + mOverlayCandidates(), + mZOrderConfig(), + mFrameBufferTarget(NULL), + mDisplayIndex(disp), + mLayerSize(0) +{ + initialize(); +} + +HwcLayerList::~HwcLayerList() +{ + deinitialize(); +} + +bool HwcLayerList::checkSupported(int planeType, HwcLayer *hwcLayer) +{ + bool valid = false; + hwc_layer_1_t& layer = *(hwcLayer->getLayer()); + + // if layer was forced to use FB + if (hwcLayer->getType() == HwcLayer::LAYER_FORCE_FB) { + VTRACE("layer was forced to use HWC_FRAMEBUFFER"); + return false; + } + + // check layer flags + if (layer.flags & HWC_SKIP_LAYER) { + VTRACE("plane type %d: (skip layer flag was set)", planeType); + return false; + } + + if (layer.handle == 0) { + WTRACE("invalid buffer handle"); + return false; + } + + // check usage + if (!hwcLayer->getUsage() & GRALLOC_USAGE_HW_COMPOSER) { + WTRACE("not a composer layer"); + return false; + } + + // check layer transform + valid = PlaneCapabilities::isTransformSupported(planeType, hwcLayer); + if (!valid) { + VTRACE("plane type %d: (bad transform)", planeType); + return false; + } + + // check buffer format + valid = PlaneCapabilities::isFormatSupported(planeType, hwcLayer); + if (!valid) { + VTRACE("plane type %d: (bad buffer format)", planeType); + return false; + } + + // check buffer size + valid = PlaneCapabilities::isSizeSupported(planeType, hwcLayer); + if (!valid) { + VTRACE("plane type %d: (bad buffer size)", planeType); + return false; + } + + // check layer blending + valid = PlaneCapabilities::isBlendingSupported(planeType, hwcLayer); + if (!valid) { + VTRACE("plane type %d: (bad blending)", planeType); + return false; + } + + // check layer scaling + valid = PlaneCapabilities::isScalingSupported(planeType, hwcLayer); + if (!valid) { + VTRACE("plane type %d: (bad scaling)", planeType); + return false; + } + + // TODO: check visible region? + return true; +} + +bool HwcLayerList::checkCursorSupported(HwcLayer *hwcLayer) +{ + hwc_layer_1_t& layer = *(hwcLayer->getLayer()); + + // if layer was forced to use FB + if (hwcLayer->getType() == HwcLayer::LAYER_FORCE_FB) { + VTRACE("layer was forced to use HWC_FRAMEBUFFER"); + return false; + } + + // check layer flags + if (layer.flags & HWC_SKIP_LAYER) { + VTRACE("skip layer flag was set"); + return false; + } + + if (!(layer.flags & HWC_IS_CURSOR_LAYER)) { + VTRACE("not a cursor layer"); + return false; + } + + if (hwcLayer->getIndex() != mLayerCount - 2) { + WTRACE("cursor layer is not on top of zorder"); + return false; + } + + if (layer.handle == 0) { + WTRACE("invalid buffer handle"); + return false; + } + + // check usage + if (!(hwcLayer->getUsage() & GRALLOC_USAGE_HW_COMPOSER)) { + WTRACE("not a composer layer"); + return false; + } + + uint32_t format = hwcLayer->getFormat(); + if (format != HAL_PIXEL_FORMAT_BGRA_8888 && + format != HAL_PIXEL_FORMAT_RGBA_8888) { + WTRACE("unexpected color format %u for cursor", format); + return false; + } + + uint32_t trans = hwcLayer->getLayer()->transform; + if (trans != 0) { + WTRACE("unexpected transform %u for cursor", trans); + return false; + } + + hwc_frect_t& src = hwcLayer->getLayer()->sourceCropf; + hwc_rect_t& dest = hwcLayer->getLayer()->displayFrame; + int srcW = (int)src.right - (int)src.left; + int srcH = (int)src.bottom - (int)src.top; + int dstW = dest.right - dest.left; + int dstH = dest.bottom - dest.top; + if (srcW != dstW || srcH != dstH) { + WTRACE("unexpected scaling for cursor: %dx%d => %dx%d", + srcW, srcH, dstW, dstH); + //return false; + } + + if (srcW > 256 || srcH > 256) { + WTRACE("unexpected size %dx%d for cursor", srcW, srcH); + return false; + } + + BufferManager *bm = Hwcomposer::getInstance().getBufferManager(); + if (bm) { + DataBuffer *buffer = bm->lockDataBuffer(hwcLayer->getHandle()); + if (buffer) { + uint32_t w = buffer->getWidth(); + uint32_t h = buffer->getHeight(); + + if ((w != 64 || h != 64) && + (w != 128 || h != 128) && + (w != 256 || h != 256)) { + bm->unlockDataBuffer(buffer); + return false; + } + } + bm->unlockDataBuffer(buffer); + } + + return true; +} + +bool HwcLayerList::initialize() +{ + if (!mList || mList->numHwLayers == 0) { + ETRACE("invalid hwc list"); + return false; + } + + mLayerCount = (int)mList->numHwLayers; + mLayers.setCapacity(mLayerCount); + mFBLayers.setCapacity(mLayerCount); + mSpriteCandidates.setCapacity(mLayerCount); + mOverlayCandidates.setCapacity(mLayerCount); + mCursorCandidates.setCapacity(mLayerCount); + mZOrderConfig.setCapacity(mLayerCount); + Hwcomposer& hwc = Hwcomposer::getInstance(); + + for (int i = 0; i < mLayerCount; i++) { + hwc_layer_1_t *layer = &mList->hwLayers[i]; + if (!layer) { + DEINIT_AND_RETURN_FALSE("layer %d is null", i); + } + + HwcLayer *hwcLayer = new HwcLayer(i, layer); + if (!hwcLayer) { + DEINIT_AND_RETURN_FALSE("failed to allocate hwc layer %d", i); + } + + if (layer->compositionType == HWC_FRAMEBUFFER_TARGET) { + hwcLayer->setType(HwcLayer::LAYER_FRAMEBUFFER_TARGET); + mFrameBufferTarget = hwcLayer; + } else if (layer->compositionType == HWC_OVERLAY){ + // skipped layer, filtered by Display Analyzer + hwcLayer->setType(HwcLayer::LAYER_SKIPPED); + } else if (layer->compositionType == HWC_FORCE_FRAMEBUFFER) { + layer->compositionType = HWC_FRAMEBUFFER; + hwcLayer->setType(HwcLayer::LAYER_FORCE_FB); + // add layer to FB layer list for zorder check during plane assignment + mFBLayers.add(hwcLayer); + } else if (layer->compositionType == HWC_FRAMEBUFFER) { + // by default use GPU composition + hwcLayer->setType(HwcLayer::LAYER_FB); + mFBLayers.add(hwcLayer); + if (checkCursorSupported(hwcLayer)) { + mCursorCandidates.add(hwcLayer); + } else if (checkSupported(DisplayPlane::PLANE_SPRITE, hwcLayer)) { + mSpriteCandidates.add(hwcLayer); + } else if (hwc.getDisplayAnalyzer()->isOverlayAllowed() && + checkSupported(DisplayPlane::PLANE_OVERLAY, hwcLayer)) { + mOverlayCandidates.add(hwcLayer); + } else { + // noncandidate layer + } + } else if (layer->compositionType == HWC_SIDEBAND){ + hwcLayer->setType(HwcLayer::LAYER_SIDEBAND); + } else { + DEINIT_AND_RETURN_FALSE("invalid composition type %d", layer->compositionType); + } + // add layer to layer list + mLayers.add(hwcLayer); + } + + if (mFrameBufferTarget == NULL) { + ETRACE("no frame buffer target?"); + return false; + } + + // If has layer besides of FB_Target, but no FBLayers, skip plane allocation + // Note: There is case that SF passes down a layerlist with only FB_Target + // layer; we need to have this FB_Target to be flipped as well, otherwise it + // will have the buffer queue blocked. (The buffer hold by driver cannot be + // released if new buffers' flip is skipped). + if ((mFBLayers.size() == 0) && (mLayers.size() > 1)) { + VTRACE("no FB layers, skip plane allocation"); + return true; + } + + allocatePlanes(); + + //dump(); + return true; +} + +void HwcLayerList::deinitialize() +{ + if (mLayerCount == 0) { + return; + } + + DisplayPlaneManager *planeManager = Hwcomposer::getInstance().getPlaneManager(); + for (int i = 0; i < mLayerCount; i++) { + HwcLayer *hwcLayer = mLayers.itemAt(i); + if (hwcLayer) { + DisplayPlane *plane = hwcLayer->detachPlane(); + if (plane) { + planeManager->reclaimPlane(mDisplayIndex, *plane); + } + } + delete hwcLayer; + } + + mLayers.clear(); + mFBLayers.clear(); + mOverlayCandidates.clear(); + mSpriteCandidates.clear(); + mCursorCandidates.clear(); + mZOrderConfig.clear(); + mFrameBufferTarget = NULL; + mLayerCount = 0; +} + + +bool HwcLayerList::allocatePlanes() +{ + return assignCursorPlanes(); +} + +bool HwcLayerList::assignCursorPlanes() +{ + int cursorCandidates = (int)mCursorCandidates.size(); + if (cursorCandidates == 0) { + return assignOverlayPlanes(); + } + + DisplayPlaneManager *planeManager = Hwcomposer::getInstance().getPlaneManager(); + int planeNumber = planeManager->getFreePlanes(mDisplayIndex, DisplayPlane::PLANE_CURSOR); + if (planeNumber == 0) { + DTRACE("no cursor plane available. candidates %d", cursorCandidates); + return assignOverlayPlanes(); + } + + if (planeNumber > cursorCandidates) { + // assuming all cursor planes have the same capabilities, just + // need up to number of candidates for plane assignment + planeNumber = cursorCandidates; + } + + for (int i = planeNumber; i >= 0; i--) { + // assign as many cursor planes as possible + if (assignCursorPlanes(0, i)) { + return true; + } + if (mZOrderConfig.size() != 0) { + ETRACE("ZOrder config is not cleaned up!"); + } + } + return false; +} + +bool HwcLayerList::assignCursorPlanes(int index, int planeNumber) +{ + // index indicates position in mCursorCandidates to start plane assignment + if (planeNumber == 0) { + return assignOverlayPlanes(); + } + + int cursorCandidates = (int)mCursorCandidates.size(); + for (int i = index; i <= cursorCandidates - planeNumber; i++) { + ZOrderLayer *zlayer = addZOrderLayer(DisplayPlane::PLANE_CURSOR, mCursorCandidates[i]); + if (assignCursorPlanes(i + 1, planeNumber - 1)) { + return true; + } + removeZOrderLayer(zlayer); + } + return false; +} + +bool HwcLayerList::assignOverlayPlanes() +{ + int overlayCandidates = (int)mOverlayCandidates.size(); + if (overlayCandidates == 0) { + return assignSpritePlanes(); + } + + DisplayPlaneManager *planeManager = Hwcomposer::getInstance().getPlaneManager(); + int planeNumber = planeManager->getFreePlanes(mDisplayIndex, DisplayPlane::PLANE_OVERLAY); + if (planeNumber == 0) { + DTRACE("no overlay plane available. candidates %d", overlayCandidates); + return assignSpritePlanes(); + } + + if (planeNumber > overlayCandidates) { + // assuming all overlay planes have the same capabilities, just + // need up to number of candidates for plane assignment + planeNumber = overlayCandidates; + } + + for (int i = planeNumber; i >= 0; i--) { + // assign as many overlay planes as possible + if (assignOverlayPlanes(0, i)) { + return true; + } + if (mZOrderConfig.size() != 0) { + ETRACE("ZOrder config is not cleaned up!"); + } + } + return false; +} + + +bool HwcLayerList::assignOverlayPlanes(int index, int planeNumber) +{ + // index indicates position in mOverlayCandidates to start plane assignment + if (planeNumber == 0) { + return assignSpritePlanes(); + } + + int overlayCandidates = (int)mOverlayCandidates.size(); + for (int i = index; i <= overlayCandidates - planeNumber; i++) { + ZOrderLayer *zlayer = addZOrderLayer(DisplayPlane::PLANE_OVERLAY, mOverlayCandidates[i]); + if (assignOverlayPlanes(i + 1, planeNumber - 1)) { + return true; + } + removeZOrderLayer(zlayer); + } + return false; +} + +bool HwcLayerList::assignSpritePlanes() +{ + int spriteCandidates = (int)mSpriteCandidates.size(); + if (spriteCandidates == 0) { + return assignPrimaryPlane(); + } + + // number does not include primary plane + DisplayPlaneManager *planeManager = Hwcomposer::getInstance().getPlaneManager(); + int planeNumber = planeManager->getFreePlanes(mDisplayIndex, DisplayPlane::PLANE_SPRITE); + if (planeNumber == 0) { + VTRACE("no sprite plane available, candidates %d", spriteCandidates); + return assignPrimaryPlane(); + } + + if (planeNumber > spriteCandidates) { + // assuming all sprite planes have the same capabilities, just + // need up to number of candidates for plane assignment + planeNumber = spriteCandidates; + } + + for (int i = planeNumber; i >= 0; i--) { + // assign as many sprite planes as possible + if (assignSpritePlanes(0, i)) { + return true; + } + + if (mOverlayCandidates.size() == 0 && mZOrderConfig.size() != 0) { + ETRACE("ZOrder config is not cleaned up!"); + } + } + return false; +} + + +bool HwcLayerList::assignSpritePlanes(int index, int planeNumber) +{ + if (planeNumber == 0) { + return assignPrimaryPlane(); + } + + int spriteCandidates = (int)mSpriteCandidates.size(); + for (int i = index; i <= spriteCandidates - planeNumber; i++) { + ZOrderLayer *zlayer = addZOrderLayer(DisplayPlane::PLANE_SPRITE, mSpriteCandidates[i]); + if (assignSpritePlanes(i + 1, planeNumber - 1)) { + return true; + } + removeZOrderLayer(zlayer); + } + return false; +} + +bool HwcLayerList::assignPrimaryPlane() +{ + // find a sprit layer that is not candidate but has lower priority than candidates. + HwcLayer *spriteLayer = NULL; + for (int i = (int)mSpriteCandidates.size() - 1; i >= 0; i--) { + if (mSpriteCandidates[i]->mPlaneCandidate) + break; + + spriteLayer = mSpriteCandidates[i]; + } + + int candidates = (int)mZOrderConfig.size(); + int layers = (int)mFBLayers.size(); + bool ok = false; + + if (candidates == layers - 1 && spriteLayer != NULL) { + // primary plane is configured as sprite, all sprite candidates are offloaded to display planes + ok = assignPrimaryPlaneHelper(spriteLayer); + if (!ok) { + VTRACE("failed to use primary as sprite plane"); + } + } else if (candidates == 0) { + // none assigned, use primary plane for frame buffer target and set zorder to 0 + ok = assignPrimaryPlaneHelper(mFrameBufferTarget, 0); + if (!ok) { + ETRACE("failed to compose all layers to primary plane, should never happen"); + } + } else if (candidates == layers) { + // all assigned, primary plane may be used during ZOrder config. + ok = attachPlanes(); + if (!ok) { + VTRACE("failed to assign layers without primary"); + } + } else { + // check if the remaining planes can be composed to frame buffer target (FBT) + // look up a legitimate Z order position to place FBT. + for (int i = 0; i < layers && !ok; i++) { + if (mFBLayers[i]->mPlaneCandidate) { + continue; + } + if (useAsFrameBufferTarget(mFBLayers[i])) { + ok = assignPrimaryPlaneHelper(mFrameBufferTarget, mFBLayers[i]->getZOrder()); + if (!ok) { + VTRACE("failed to use zorder %d for frame buffer target", + mFBLayers[i]->getZOrder()); + } + } + } + if (!ok) { + VTRACE("no possible zorder for frame buffer target"); + } + + } + return ok; +} + +bool HwcLayerList::assignPrimaryPlaneHelper(HwcLayer *hwcLayer, int zorder) +{ + ZOrderLayer *zlayer = addZOrderLayer(DisplayPlane::PLANE_PRIMARY, hwcLayer, zorder); + bool ok = attachPlanes(); + if (!ok) { + removeZOrderLayer(zlayer); + } + return ok; +} + +bool HwcLayerList::attachPlanes() +{ + DisplayPlaneManager *planeManager = Hwcomposer::getInstance().getPlaneManager(); + if (!planeManager->isValidZOrder(mDisplayIndex, mZOrderConfig)) { + VTRACE("invalid z order, size of config %d", mZOrderConfig.size()); + return false; + } + + if (!planeManager->assignPlanes(mDisplayIndex, mZOrderConfig)) { + WTRACE("failed to assign planes"); + return false; + } + + VTRACE("============= plane assignment==================="); + for (int i = 0; i < (int)mZOrderConfig.size(); i++) { + ZOrderLayer *zlayer = mZOrderConfig.itemAt(i); + if (zlayer->plane == NULL || zlayer->hwcLayer == NULL) { + ETRACE("invalid ZOrderLayer, should never happen!!"); + return false; + } + + zlayer->plane->setZOrder(i); + + if (zlayer->plane->getType() == DisplayPlane::PLANE_CURSOR) { + zlayer->hwcLayer->setType(HwcLayer::LAYER_CURSOR_OVERLAY); + mFBLayers.remove(zlayer->hwcLayer); + } else if (zlayer->hwcLayer != mFrameBufferTarget) { + zlayer->hwcLayer->setType(HwcLayer::LAYER_OVERLAY); + // update FB layers for smart composition + mFBLayers.remove(zlayer->hwcLayer); + } + + zlayer->hwcLayer->attachPlane(zlayer->plane, mDisplayIndex); + + VTRACE("total %d, layer %d, type %d, index %d, zorder %d", + mLayerCount - 1, + zlayer->hwcLayer->getIndex(), + zlayer->plane->getType(), + zlayer->plane->getIndex(), + zlayer->zorder); + + delete zlayer; + } + + mZOrderConfig.clear(); + return true; +} + +bool HwcLayerList::useAsFrameBufferTarget(HwcLayer *target) +{ + // check if zorder of target can be used as zorder of frame buffer target + // eligible only when all noncandidate layers can be merged to the target layer: + // 1) noncandidate layer and candidate layer below the target layer can't overlap + // if candidate layer is on top of non candidate layer, as "noncandidate layer" needs + // to be moved up to target layer in z order; + // 2) noncandidate layer and candidate layers above the target layer can't overlap + // if candidate layer is below noncandidate layer, as "noncandidate layer" needs + // to be moved down to target layer in z order. + + int targetLayerIndex = target->getIndex(); + + // check candidate and noncandidate layers below this candidate does not overlap + for (int below = 0; below < targetLayerIndex; below++) { + if (mFBLayers[below]->mPlaneCandidate) { + continue; + } else { + // check candidate layer above this noncandidate layer does not overlap + for (int above = below + 1; above < targetLayerIndex; above++) { + if (mFBLayers[above]->mPlaneCandidate == false) { + continue; + } + if (hasIntersection(mFBLayers[above], mFBLayers[below])) { + return false; + } + } + } + } + + // check candidate and noncandidate layers above this candidate does not overlap + for (int above = targetLayerIndex + 1; above < mFBLayers.size(); above++) { + if (mFBLayers[above]->mPlaneCandidate) { + continue; + } else { + // check candidate layer below this noncandidate layer does not overlap + for (int below = targetLayerIndex + 1; below < above; below++) { + if (mFBLayers[below]->mPlaneCandidate == false) { + continue; + } + if (hasIntersection(mFBLayers[above], mFBLayers[below])) { + return false; + } + } + } + } + + return true; +} + +bool HwcLayerList::hasIntersection(HwcLayer *la, HwcLayer *lb) +{ + hwc_layer_1_t *a = la->getLayer(); + hwc_layer_1_t *b = lb->getLayer(); + hwc_rect_t *aRect = &a->displayFrame; + hwc_rect_t *bRect = &b->displayFrame; + + if (bRect->right <= aRect->left || + bRect->left >= aRect->right || + bRect->top >= aRect->bottom || + bRect->bottom <= aRect->top) + return false; + + return true; +} + +ZOrderLayer* HwcLayerList::addZOrderLayer(int type, HwcLayer *hwcLayer, int zorder) +{ + ZOrderLayer *layer = new ZOrderLayer; + layer->planeType = type; + layer->hwcLayer = hwcLayer; + layer->zorder = (zorder != -1) ? zorder : hwcLayer->getZOrder(); + layer->plane = NULL; + + if (hwcLayer->mPlaneCandidate) { + ETRACE("plane is candidate!, order = %d", zorder); + } + + hwcLayer->mPlaneCandidate = true; + + if ((int)mZOrderConfig.indexOf(layer) >= 0) { + ETRACE("layer exists!"); + } + + mZOrderConfig.add(layer); + return layer; +} + +void HwcLayerList::removeZOrderLayer(ZOrderLayer *layer) +{ + if ((int)mZOrderConfig.indexOf(layer) < 0) { + ETRACE("layer does not exist!"); + } + + mZOrderConfig.remove(layer); + + if (layer->hwcLayer->mPlaneCandidate == false) { + ETRACE("plane is not candidate!, order %d", layer->zorder); + } + layer->hwcLayer->mPlaneCandidate = false; + delete layer; +} + +void HwcLayerList::addStaticLayerSize(HwcLayer *hwcLayer) +{ + // Calculate static layer size to avoid only composition navigation bar + // and status bar etc. + hwc_layer_1_t *a = hwcLayer->getLayer(); + hwc_rect_t *Rect = &a->displayFrame; + + mLayerSize = mLayerSize + ((Rect->right - Rect->left) * (Rect->bottom - Rect->top)); +} + +bool HwcLayerList::checkStaticLayerSize() +{ + // Check static layer size if over threshold: half display size + bool ret = false; + int width = 0; + int height = 0; + drmModeModeInfo mode; + Drm *drm = Hwcomposer::getInstance().getDrm(); + drm->getModeInfo(mDisplayIndex, mode); + width = mode.hdisplay; + height = mode.vdisplay; + + if (mLayerSize > (width * height/2)) + ret = true; + + return ret; +} + +void HwcLayerList::setupSmartComposition() +{ + uint32_t compositionType = HWC_OVERLAY; + HwcLayer *hwcLayer = NULL; + + // setup smart composition only there's no update on all FB layers + for (size_t i = 0; i < mFBLayers.size(); i++) { + hwcLayer = mFBLayers.itemAt(i); + if (hwcLayer->isUpdated() || + hwcLayer->getStaticCount() == LAYER_STATIC_THRESHOLD) { + compositionType = HWC_FRAMEBUFFER; + } + } + + VTRACE("smart composition enabled %s", + (compositionType == HWC_OVERLAY) ? "TRUE" : "FALSE"); + for (size_t i = 0; i < mFBLayers.size(); i++) { + hwcLayer = mFBLayers.itemAt(i); + switch (hwcLayer->getType()) { + case HwcLayer::LAYER_FB: + case HwcLayer::LAYER_FORCE_FB: + hwcLayer->setCompositionType(compositionType); + break; + default: + ETRACE("Invalid layer type %d", hwcLayer->getType()); + break; + } + } +} + +bool HwcLayerList::setupSmartComposition2() +{ + bool ret = false; + HwcLayer *hwcLayer = NULL; + int layerIndex = 0; + int i = 0; + + if (mList->flags & HWC_GEOMETRY_CHANGED) { + // clear static layers vector once geometry changed + mStaticLayersIndex.setCapacity(mLayerCount); + mStaticLayersIndex.clear(); + return ret; + } + + if (mStaticLayersIndex.size() > 0) { + // exit criteria: once either static layer has update + for (i = 0; i < mStaticLayersIndex.size(); i++) { + layerIndex = mStaticLayersIndex.itemAt(i); + hwcLayer = mLayers.itemAt(layerIndex); + + if (hwcLayer->isUpdated()) { + ret = true; + } + } + + if (ret == true) { + for (i = 0; i < mStaticLayersIndex.size(); i++) { + layerIndex = mStaticLayersIndex.itemAt(i); + hwcLayer = mLayers.itemAt(layerIndex); + + hwcLayer->setCompositionType(HWC_FRAMEBUFFER); + } + + DTRACE("Exit Smart Composition2 !"); + mLayerSize = 0; + mStaticLayersIndex.clear(); + } + } else { + // entry criteria: hwc layers has no update + if (mFBLayers.size() == 0) { + for (i = 0; i < mLayerCount - 1; i++) { + hwcLayer = mLayers.itemAt(i); + if (hwcLayer->getPlane() && + hwcLayer->getCompositionType() == HWC_OVERLAY && + hwcLayer->getStaticCount() >= LAYER_STATIC_THRESHOLD) { + mStaticLayersIndex.add(i); + } + } + + // check if all static layers in sequence + // if all in sequence, set FORCE_FB for static layers + // TODO: optimization here + // 1. If two connected, can trigger smart composition2 + // 2. Caculate layer size to see if it saves more bandwidth + // 3. Dynamically check and add new static layers + int staticLayerCount = mStaticLayersIndex.size(); + + if (staticLayerCount > 1 && staticLayerCount < mLayerCount-1) { + layerIndex = mStaticLayersIndex.itemAt(0); + hwcLayer = mLayers.itemAt(layerIndex); + mLayerSize = 0; + addStaticLayerSize(hwcLayer); + int preIndex = hwcLayer->getIndex(); + + for (i = 1; i < staticLayerCount; i++) { + layerIndex = mStaticLayersIndex.itemAt(i); + hwcLayer = mLayers.itemAt(layerIndex); + int index = hwcLayer->getIndex(); + + if (index == preIndex + 1) { + addStaticLayerSize(hwcLayer); + preIndex = index; + } else + break; + } + + if ((i == staticLayerCount) && checkStaticLayerSize()) { + for (i =0; i < staticLayerCount; i++) { + layerIndex = mStaticLayersIndex.itemAt(i); + hwcLayer = mLayers.itemAt(layerIndex); + hwcLayer->setCompositionType(HWC_FORCE_FRAMEBUFFER); + } + DTRACE("In Smart Composition2 !"); + ret = true; + } else { + mLayerSize = 0; + } + } + + if (!ret) + mStaticLayersIndex.clear(); + } + } + + // return ture to trigger remap layers with HW plane + return ret; +} + +#if 1 // support overlay fallback to GLES + +bool HwcLayerList::update(hwc_display_contents_1_t *list) +{ + bool ret; + + // basic check to make sure the consistance + if (!list) { + ETRACE("null layer list"); + return false; + } + + if ((int)list->numHwLayers != mLayerCount) { + ETRACE("layer count doesn't match (%zd, %d)", list->numHwLayers, mLayerCount); + return false; + } + + // update list + mList = list; + + bool ok = true; + // update all layers, call each layer's update() + for (int i = 0; i < mLayerCount; i++) { + HwcLayer *hwcLayer = mLayers.itemAt(i); + if (!hwcLayer) { + ETRACE("no HWC layer for layer %d", i); + continue; + } + + if (!hwcLayer->update(&list->hwLayers[i])) { + ok = false; + hwcLayer->setCompositionType(HWC_FORCE_FRAMEBUFFER); + } + } + + if (!ok || setupSmartComposition2()) { + ITRACE("overlay fallback to GLES. flags: %#x", list->flags); + for (int i = 0; i < mLayerCount - 1; i++) { + HwcLayer *hwcLayer = mLayers.itemAt(i); + if (hwcLayer->getPlane() && + (hwcLayer->getCompositionType() == HWC_OVERLAY || + hwcLayer->getCompositionType() == HWC_CURSOR_OVERLAY)) { + hwcLayer->setCompositionType(HWC_FRAMEBUFFER); + } + } + mLayers.itemAt(mLayerCount - 1)->setCompositionType(HWC_FRAMEBUFFER_TARGET); + deinitialize(); + mList = list; + initialize(); + + // update all layers again after plane re-allocation + for (int i = 0; i < mLayerCount; i++) { + HwcLayer *hwcLayer = mLayers.itemAt(i); + if (!hwcLayer) { + ETRACE("no HWC layer for layer %d", i); + continue; + } + + if (!hwcLayer->update(&list->hwLayers[i])) { + DTRACE("fallback to GLES update failed on layer[%d]!\n", i); + } + } + } + + setupSmartComposition(); + return true; +} + +#else + +bool HwcLayerList::update(hwc_display_contents_1_t *list) +{ + bool ret; + + // basic check to make sure the consistance + if (!list) { + ETRACE("null layer list"); + return false; + } + + if ((int)list->numHwLayers != mLayerCount) { + ETRACE("layer count doesn't match (%d, %d)", list->numHwLayers, mLayerCount); + return false; + } + + // update list + mList = list; + + // update all layers, call each layer's update() + for (int i = 0; i < mLayerCount; i++) { + HwcLayer *hwcLayer = mLayers.itemAt(i); + if (!hwcLayer) { + ETRACE("no HWC layer for layer %d", i); + continue; + } + + hwcLayer->update(&list->hwLayers[i]); + } + + setupSmartComposition(); + return true; +} + +#endif + +DisplayPlane* HwcLayerList::getPlane(uint32_t index) const +{ + HwcLayer *hwcLayer; + + if (index >= mLayers.size()) { + ETRACE("invalid layer index %d", index); + return 0; + } + + hwcLayer = mLayers.itemAt(index); + if ((hwcLayer->getType() == HwcLayer::LAYER_FB) || + (hwcLayer->getType() == HwcLayer::LAYER_FORCE_FB) || + (hwcLayer->getType() == HwcLayer::LAYER_SKIPPED)) { + return 0; + } + + if (hwcLayer->getHandle() == 0) { + DTRACE("plane is attached with invalid handle"); + return 0; + } + + return hwcLayer->getPlane(); +} + +void HwcLayerList::postFlip() +{ + for (size_t i = 0; i < mLayers.size(); i++) { + HwcLayer *hwcLayer = mLayers.itemAt(i); + hwcLayer->postFlip(); + } +} + +void HwcLayerList::dump(Dump& d) +{ + d.append("Layer list: (number of layers %d):\n", mLayers.size()); + d.append(" LAYER | TYPE | PLANE | INDEX | Z Order \n"); + d.append("-------+------------------------+----------------------------\n"); + for (size_t i = 0; i < mLayers.size(); i++) { + HwcLayer *hwcLayer = mLayers.itemAt(i); + DisplayPlane *plane; + long int planeIndex = -1; + long int zorder = -1; + const char *type = "HWC_FB"; + const char *planeType = "N/A"; + + if (hwcLayer) { + switch (hwcLayer->getType()) { + case HwcLayer::LAYER_FB: + case HwcLayer::LAYER_FORCE_FB: + type = "HWC_FB"; + break; + case HwcLayer::LAYER_OVERLAY: + case HwcLayer::LAYER_SKIPPED: + type = "HWC_OVERLAY"; + break; + case HwcLayer::LAYER_FRAMEBUFFER_TARGET: + type = "HWC_FRAMEBUFFER_TARGET"; + break; + case HwcLayer::LAYER_SIDEBAND: + type = "HWC_SIDEBAND"; + break; + case HwcLayer::LAYER_CURSOR_OVERLAY: + type = "HWC_CURSOR_OVERLAY"; + break; + default: + type = "Unknown"; + } + + plane = hwcLayer->getPlane(); + if (plane) { + planeIndex = plane->getIndex(); + zorder = plane->getZOrder(); + switch (plane->getType()) { + case DisplayPlane::PLANE_OVERLAY: + planeType = "OVERLAY"; + break; + case DisplayPlane::PLANE_SPRITE: + planeType = "SPRITE"; + break; + case DisplayPlane::PLANE_PRIMARY: + planeType = "PRIMARY"; + break; + case DisplayPlane::PLANE_CURSOR: + planeType = "CURSOR"; + break; + default: + planeType = "Unknown"; + } + } + + d.append(" %2d | %22s | %8s | %3ld | %3ld \n", + i, type, planeType, planeIndex, zorder); + } + } +} + + +void HwcLayerList::dump() +{ + static char const* compositionTypeName[] = { + "GLES", + "HWC", + "BG", + "FBT", + "SB", + "CUR", + "N/A"}; + + static char const* planeTypeName[] = { + "SPRITE", + "OVERLAY", + "PRIMARY", + "CURSOR", + "UNKNOWN"}; + + DTRACE(" numHwLayers = %zu, flags = %08x", mList->numHwLayers, mList->flags); + + DTRACE(" type | handle | hints | flags | tr | blend | alpha | format | source crop | frame | index | zorder | plane "); + DTRACE("------+----------+-------+-------+----+-------+-------+----------+-----------------------------------+---------------------------+-------+--------+---------"); + + + for (int i = 0 ; i < mLayerCount ; i++) { + const hwc_layer_1_t&l = mList->hwLayers[i]; + DisplayPlane *plane = mLayers[i]->getPlane(); + int planeIndex = -1; + int zorder = -1; + const char *planeType = "N/A"; + if (plane) { + planeIndex = plane->getIndex(); + zorder = plane->getZOrder(); + planeType = planeTypeName[plane->getType()]; + } + + DTRACE( + " %4s | %p | %5x | %5x | %2x | %5x | %5x | %8x | [%7.1f,%7.1f,%7.1f,%7.1f] | [%5d,%5d,%5d,%5d] | %5d | %6d | %7s ", + compositionTypeName[l.compositionType], + mLayers[i]->getHandle(), l.hints, l.flags, l.transform, l.blending, l.planeAlpha, mLayers[i]->getFormat(), + l.sourceCropf.left, l.sourceCropf.top, l.sourceCropf.right, l.sourceCropf.bottom, + l.displayFrame.left, l.displayFrame.top, l.displayFrame.right, l.displayFrame.bottom, + planeIndex, zorder, planeType); + } + +} + + +} // namespace intel +} // namespace android diff --git a/merrifield/common/base/HwcLayerList.h b/merrifield/common/base/HwcLayerList.h new file mode 100644 index 0000000..d4eb9b9 --- /dev/null +++ b/merrifield/common/base/HwcLayerList.h @@ -0,0 +1,112 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef HWC_LAYER_LIST_H +#define HWC_LAYER_LIST_H + +#include <Dump.h> +#include <hardware/hwcomposer.h> +#include <utils/SortedVector.h> +#include <DataBuffer.h> +#include <DisplayPlane.h> +#include <DisplayPlaneManager.h> +#include <HwcLayer.h> + +namespace android { +namespace intel { + + +class HwcLayerList { +public: + HwcLayerList(hwc_display_contents_1_t *list, int disp); + virtual ~HwcLayerList(); + +public: + virtual bool initialize(); + virtual void deinitialize(); + + virtual bool update(hwc_display_contents_1_t *list); + virtual DisplayPlane* getPlane(uint32_t index) const; + + void postFlip(); + + // dump interface + virtual void dump(Dump& d); + +private: + bool checkSupported(int planeType, HwcLayer *hwcLayer); + bool checkCursorSupported(HwcLayer *hwcLayer); + bool allocatePlanes(); + bool assignCursorPlanes(); + bool assignCursorPlanes(int index, int planeNumber); + bool assignOverlayPlanes(); + bool assignOverlayPlanes(int index, int planeNumber); + bool assignSpritePlanes(); + bool assignSpritePlanes(int index, int planeNumber); + bool assignPrimaryPlane(); + bool assignPrimaryPlaneHelper(HwcLayer *hwcLayer, int zorder = -1); + bool attachPlanes(); + bool useAsFrameBufferTarget(HwcLayer *target); + bool hasIntersection(HwcLayer *la, HwcLayer *lb); + void addStaticLayerSize(HwcLayer *hwcLayer); + bool checkStaticLayerSize(); + ZOrderLayer* addZOrderLayer(int type, HwcLayer *hwcLayer, int zorder = -1); + void removeZOrderLayer(ZOrderLayer *layer); + void setupSmartComposition(); + bool setupSmartComposition2(); + void dump(); + +private: + class HwcLayerVector : public SortedVector<HwcLayer*> { + public: + HwcLayerVector() {} + virtual int do_compare(const void* lhs, const void* rhs) const { + const HwcLayer* l = *(HwcLayer**)lhs; + const HwcLayer* r = *(HwcLayer**)rhs; + // sorted from index 0 to n + return l->getIndex() - r->getIndex(); + } + }; + + class PriorityVector : public SortedVector<HwcLayer*> { + public: + PriorityVector() {} + virtual int do_compare(const void* lhs, const void* rhs) const { + const HwcLayer* l = *(HwcLayer**)lhs; + const HwcLayer* r = *(HwcLayer**)rhs; + return r->getPriority() - l->getPriority(); + } + }; + + hwc_display_contents_1_t *mList; + int mLayerCount; + + HwcLayerVector mLayers; + HwcLayerVector mFBLayers; + Vector<int> mStaticLayersIndex; + PriorityVector mSpriteCandidates; + PriorityVector mOverlayCandidates; + PriorityVector mCursorCandidates; + ZOrderConfig mZOrderConfig; + HwcLayer *mFrameBufferTarget; + int mDisplayIndex; + int mLayerSize; +}; + +} // namespace intel +} // namespace android + + +#endif /* HWC_LAYER_LIST_H */ diff --git a/merrifield/common/base/HwcModule.cpp b/merrifield/common/base/HwcModule.cpp new file mode 100644 index 0000000..5894b9b --- /dev/null +++ b/merrifield/common/base/HwcModule.cpp @@ -0,0 +1,299 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <hardware/hardware.h> +#include <string.h> +#include <stdio.h> +#include <fcntl.h> +#include <errno.h> +#include <HwcTrace.h> +#include <Hwcomposer.h> + +#define GET_HWC_RETURN_X_IF_NULL(X) \ + CTRACE(); \ + Hwcomposer *hwc = static_cast<Hwcomposer*>(dev); \ + do {\ + if (!hwc) { \ + ETRACE("invalid HWC device."); \ + return X; \ + } \ + } while (0) + + +#define GET_HWC_RETURN_ERROR_IF_NULL() GET_HWC_RETURN_X_IF_NULL(-EINVAL) +#define GET_HWC_RETURN_VOID_IF_NULL() GET_HWC_RETURN_X_IF_NULL() + + +namespace android { +namespace intel { + +static int hwc_prepare(struct hwc_composer_device_1 *dev, + size_t numDisplays, + hwc_display_contents_1_t** displays) +{ + GET_HWC_RETURN_ERROR_IF_NULL(); + if (!hwc->prepare(numDisplays, displays)) { + ETRACE("failed to prepare"); + return -EINVAL; + } + return 0; +} + +static int hwc_set(struct hwc_composer_device_1 *dev, + size_t numDisplays, + hwc_display_contents_1_t **displays) +{ + GET_HWC_RETURN_ERROR_IF_NULL(); + if (!hwc->commit(numDisplays, displays)) { + ETRACE("failed to commit"); + return -EINVAL; + } + return 0; +} + +static void hwc_dump(struct hwc_composer_device_1 *dev, + char *buff, + int buff_len) +{ + GET_HWC_RETURN_VOID_IF_NULL(); + hwc->dump(buff, buff_len, 0); +} + +void hwc_registerProcs(struct hwc_composer_device_1 *dev, + hwc_procs_t const *procs) +{ + GET_HWC_RETURN_VOID_IF_NULL(); + hwc->registerProcs(procs); +} + +static int hwc_device_close(struct hw_device_t *dev) +{ + CTRACE(); + Hwcomposer::releaseInstance(); + return 0; +} + +static int hwc_query(struct hwc_composer_device_1 *dev, + int what, + int* value) +{ + ATRACE("what = %d", what); + return -EINVAL; +} + +static int hwc_eventControl(struct hwc_composer_device_1 *dev, + int disp, + int event, + int enabled) +{ + bool ret; + GET_HWC_RETURN_ERROR_IF_NULL(); + + switch (event) { + case HWC_EVENT_VSYNC: + ret = hwc->vsyncControl(disp, enabled); + if (ret == false) { + ETRACE("failed to control vsync"); + return -EINVAL; + } + break; + default: + WTRACE("unsupported event %d", event); + break; + } + + return 0; +} + +static int hwc_blank(hwc_composer_device_1_t *dev, int disp, int blank) +{ + GET_HWC_RETURN_ERROR_IF_NULL(); + bool ret = hwc->blank(disp, blank); + if (ret == false) { + ETRACE("failed to blank disp %d, blank %d", disp, blank); + return -EINVAL; + } + + return 0; +} + +static int hwc_getDisplayConfigs(hwc_composer_device_1_t *dev, + int disp, + uint32_t *configs, + size_t *numConfigs) +{ + GET_HWC_RETURN_ERROR_IF_NULL(); + bool ret = hwc->getDisplayConfigs(disp, configs, numConfigs); + if (ret == false) { + WTRACE("failed to get configs of disp %d", disp); + return -EINVAL; + } + + return 0; +} + +static int hwc_getDisplayAttributes(hwc_composer_device_1_t *dev, + int disp, + uint32_t config, + const uint32_t *attributes, + int32_t *values) +{ + GET_HWC_RETURN_ERROR_IF_NULL(); + bool ret = hwc->getDisplayAttributes(disp, config, attributes, values); + if (ret == false) { + WTRACE("failed to get attributes of disp %d", disp); + return -EINVAL; + } + + return 0; +} + +static int hwc_compositionComplete(hwc_composer_device_1_t *dev, int disp) +{ + GET_HWC_RETURN_ERROR_IF_NULL(); + bool ret = hwc->compositionComplete(disp); + if (ret == false) { + ETRACE("failed for disp %d", disp); + return -EINVAL; + } + + return 0; +} + +static int hwc_setPowerMode(hwc_composer_device_1_t *dev, int disp, int mode) +{ + GET_HWC_RETURN_ERROR_IF_NULL(); + bool ret = hwc->setPowerMode(disp, mode); + if (ret == false) { + WTRACE("failed to set power mode of disp %d", disp); + return -EINVAL; + } + + return 0; +} + +static int hwc_getActiveConfig(hwc_composer_device_1_t *dev, int disp) +{ + GET_HWC_RETURN_ERROR_IF_NULL(); + int ret = hwc->getActiveConfig(disp); + if (ret == -1) { + WTRACE("failed to get active config of disp %d", disp); + return -EINVAL; + } + + return ret; +} + +static int hwc_setActiveConfig(hwc_composer_device_1_t *dev, int disp, int index) +{ + GET_HWC_RETURN_ERROR_IF_NULL(); + bool ret = hwc->setActiveConfig(disp, index); + if (ret == false) { + WTRACE("failed to set active config of disp %d", disp); + return -EINVAL; + } + + return 0; +} + +static int hwc_setCursorPositionAsync(hwc_composer_device_1_t *dev, int disp, int x, int y) +{ + GET_HWC_RETURN_ERROR_IF_NULL(); + bool ret = hwc->setCursorPositionAsync(disp, x, y); + if (ret == false) { + WTRACE("failed to set cursor position of disp %d", disp); + return -EINVAL; + } + + return 0; +} + +//------------------------------------------------------------------------------ + +static int hwc_device_open(const struct hw_module_t* module, + const char* name, + struct hw_device_t** device) +{ + if (!name) { + ETRACE("invalid name."); + return -EINVAL; + } + + ATRACE("open device %s", name); + + if (strcmp(name, HWC_HARDWARE_COMPOSER) != 0) { + ETRACE("try to open unknown HWComposer %s", name); + return -EINVAL; + } + + Hwcomposer& hwc = Hwcomposer::getInstance(); + // initialize our state here + if (hwc.initialize() == false) { + ETRACE("failed to intialize HWComposer"); + Hwcomposer::releaseInstance(); + return -EINVAL; + } + + // initialize the procs + hwc.hwc_composer_device_1_t::common.tag = HARDWARE_DEVICE_TAG; + hwc.hwc_composer_device_1_t::common.module = + const_cast<hw_module_t*>(module); + hwc.hwc_composer_device_1_t::common.close = hwc_device_close; + + hwc.hwc_composer_device_1_t::prepare = hwc_prepare; + hwc.hwc_composer_device_1_t::set = hwc_set; + hwc.hwc_composer_device_1_t::dump = hwc_dump; + hwc.hwc_composer_device_1_t::registerProcs = hwc_registerProcs; + hwc.hwc_composer_device_1_t::query = hwc_query; + + hwc.hwc_composer_device_1_t::blank = hwc_blank; + hwc.hwc_composer_device_1_t::eventControl = hwc_eventControl; + hwc.hwc_composer_device_1_t::getDisplayConfigs = hwc_getDisplayConfigs; + hwc.hwc_composer_device_1_t::getDisplayAttributes = hwc_getDisplayAttributes; + + // This is used to hack FBO switch flush issue in SurfaceFlinger. + hwc.hwc_composer_device_1_t::reserved_proc[0] = (void*)hwc_compositionComplete; + hwc.hwc_composer_device_1_t::common.version = HWC_DEVICE_API_VERSION_1_4; + hwc.hwc_composer_device_1_t::setPowerMode = hwc_setPowerMode; + hwc.hwc_composer_device_1_t::getActiveConfig = hwc_getActiveConfig; + hwc.hwc_composer_device_1_t::setActiveConfig = hwc_setActiveConfig; + // Todo: add hwc_setCursorPositionAsync after supporting patches + hwc.hwc_composer_device_1_t::setCursorPositionAsync = NULL; + + *device = &hwc.hwc_composer_device_1_t::common; + + return 0; +} + +} // namespace intel +} // namespace android + +static struct hw_module_methods_t hwc_module_methods = { + open: android::intel::hwc_device_open +}; + +hwc_module_t HAL_MODULE_INFO_SYM = { + common: { + tag: HARDWARE_MODULE_TAG, + version_major: 1, + version_minor: 4, + id: HWC_HARDWARE_MODULE_ID, + name: "Intel Hardware Composer", + author: "Intel", + methods: &hwc_module_methods, + dso: NULL, + reserved: {0}, + } +}; diff --git a/merrifield/common/base/Hwcomposer.cpp b/merrifield/common/base/Hwcomposer.cpp new file mode 100644 index 0000000..bb08c73 --- /dev/null +++ b/merrifield/common/base/Hwcomposer.cpp @@ -0,0 +1,550 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <HwcTrace.h> +#include <Hwcomposer.h> +#include <Dump.h> +#include <UeventObserver.h> + +namespace android { +namespace intel { + +Hwcomposer* Hwcomposer::sInstance(0); + +Hwcomposer::Hwcomposer(IPlatFactory *factory) + : mProcs(0), + mDrm(0), + mPlatFactory(factory), + mVsyncManager(0), + mDisplayAnalyzer(0), + mMultiDisplayObserver(0), + mUeventObserver(0), + mPlaneManager(0), + mBufferManager(0), + mDisplayContext(0), + mInitialized(false) +{ + CTRACE(); + + mDisplayDevices.setCapacity(IDisplayDevice::DEVICE_COUNT); + mDisplayDevices.clear(); +} + +Hwcomposer::~Hwcomposer() +{ + CTRACE(); + deinitialize(); +} + +bool Hwcomposer::initCheck() const +{ + return mInitialized; +} + +bool Hwcomposer::prepare(size_t numDisplays, + hwc_display_contents_1_t** displays) +{ + bool ret = true; + + RETURN_FALSE_IF_NOT_INIT(); + ATRACE("display count = %d", numDisplays); + + if (!numDisplays || !displays) { + ETRACE("invalid parameters"); + return false; + } + + mDisplayAnalyzer->analyzeContents(numDisplays, displays); + + // disable reclaimed planes + mPlaneManager->disableReclaimedPlanes(); + + if(numDisplays > mDisplayDevices.size()) + numDisplays = mDisplayDevices.size(); + + // reclaim all allocated planes if possible + for (size_t i = 0; i < numDisplays; i++) { + IDisplayDevice *device = mDisplayDevices.itemAt(i); + if (!device) { + VTRACE("device %d doesn't exist", i); + continue; + } + + device->prePrepare(displays[i]); + } + + for (size_t i = 0; i < numDisplays; i++) { + IDisplayDevice *device = mDisplayDevices.itemAt(i); + if (!device) { + VTRACE("device %d doesn't exist", i); + continue; + } + + ret = device->prepare(displays[i]); + if (ret == false) { + ETRACE("failed to do prepare for device %d", i); + continue; + } + } + + return ret; +} + +bool Hwcomposer::commit(size_t numDisplays, + hwc_display_contents_1_t **displays) +{ + bool ret = true; + + RETURN_FALSE_IF_NOT_INIT(); + ATRACE("display count = %d", numDisplays); + + if (!numDisplays || !displays) { + ETRACE("invalid parameters"); + return false; + } + + if(numDisplays > mDisplayDevices.size()) + numDisplays = mDisplayDevices.size(); + + mDisplayContext->commitBegin(numDisplays, displays); + + for (size_t i = 0; i < numDisplays; i++) { + IDisplayDevice *device = mDisplayDevices.itemAt(i); + if (!device) { + VTRACE("device %d doesn't exist", i); + continue; + } + + if (!device->isConnected()) { + VTRACE("device %d is disconnected", i); + continue; + } + + ret = device->commit(displays[i], mDisplayContext); + if (ret == false) { + ETRACE("failed to do commit for device %d", i); + continue; + } + } + + mDisplayContext->commitEnd(numDisplays, displays); + // return true always + return true; +} + +bool Hwcomposer::setPowerMode(int disp, int mode) +{ + RETURN_FALSE_IF_NOT_INIT(); + + if (disp < 0 || disp >= IDisplayDevice::DEVICE_COUNT) { + ETRACE("invalid disp %d", disp); + return false; + } + + if(disp >= mDisplayDevices.size()){ + ETRACE("no device found"); + return false; + } + + IDisplayDevice *device = mDisplayDevices.itemAt(disp); + if (!device) { + ETRACE("no device found"); + return false; + } + + return device->setPowerMode(mode); +} + +int Hwcomposer::getActiveConfig(int disp) +{ + RETURN_NULL_IF_NOT_INIT(); + + if (disp < 0 || disp >= IDisplayDevice::DEVICE_COUNT) { + ETRACE("invalid disp %d", disp); + return -1; + } + + IDisplayDevice *device = mDisplayDevices.itemAt(disp); + if (!device) { + ETRACE("no device found"); + return -1; + } + + return device->getActiveConfig(); +} + +bool Hwcomposer::setActiveConfig(int disp, int index) +{ + RETURN_FALSE_IF_NOT_INIT(); + + if (disp < 0 || disp >= IDisplayDevice::DEVICE_COUNT) { + ETRACE("invalid disp %d", disp); + return false; + } + + IDisplayDevice *device = mDisplayDevices.itemAt(disp); + if (!device) { + ETRACE("no device found"); + return false; + } + + return device->setActiveConfig(index); +} + +bool Hwcomposer::setCursorPositionAsync(int disp, int x, int y) +{ + RETURN_FALSE_IF_NOT_INIT(); + + if (disp != HWC_DISPLAY_PRIMARY && disp != HWC_DISPLAY_EXTERNAL) { + ETRACE("invalid disp %d", disp); + return false; + } + + return mDisplayContext->setCursorPosition(disp, x, y); +} + +bool Hwcomposer::vsyncControl(int disp, int enabled) +{ + RETURN_FALSE_IF_NOT_INIT(); + ATRACE("disp = %d, enabled = %d", disp, enabled); + return mVsyncManager->handleVsyncControl(disp, enabled ? true : false); +} + +bool Hwcomposer::blank(int disp, int blank) +{ + RETURN_FALSE_IF_NOT_INIT(); + ATRACE("disp = %d, blank = %d", disp, blank); + + if (disp < 0 || disp >= IDisplayDevice::DEVICE_COUNT) { + ETRACE("invalid disp %d", disp); + return false; + } + + IDisplayDevice *device = mDisplayDevices.itemAt(disp); + if (!device) { + ETRACE("no device found"); + return false; + } + + return device->blank(blank ? true : false); +} + +bool Hwcomposer::getDisplayConfigs(int disp, + uint32_t *configs, + size_t *numConfigs) +{ + RETURN_FALSE_IF_NOT_INIT(); + + if (disp < 0 || disp >= IDisplayDevice::DEVICE_COUNT) { + ETRACE("invalid disp %d", disp); + return false; + } + + if(disp >= mDisplayDevices.size()){ + ETRACE("no device found"); + return false; + } + + IDisplayDevice *device = mDisplayDevices.itemAt(disp); + if (!device) { + ETRACE("no device %d found", disp); + return false; + } + + return device->getDisplayConfigs(configs, numConfigs); +} + +bool Hwcomposer::getDisplayAttributes(int disp, + uint32_t config, + const uint32_t *attributes, + int32_t *values) +{ + RETURN_FALSE_IF_NOT_INIT(); + + if (disp < 0 || disp >= IDisplayDevice::DEVICE_COUNT) { + ETRACE("invalid disp %d", disp); + return false; + } + if(disp >= mDisplayDevices.size()){ + ETRACE("no device found"); + return false; + } + + + IDisplayDevice *device = mDisplayDevices.itemAt(disp); + if (!device) { + ETRACE("no device found"); + return false; + } + + return device->getDisplayAttributes(config, attributes, values); +} + +bool Hwcomposer::compositionComplete(int disp) +{ + RETURN_FALSE_IF_NOT_INIT(); + + if (disp < 0 || disp >= IDisplayDevice::DEVICE_COUNT) { + ETRACE("invalid disp %d", disp); + return false; + } + + mDisplayContext->compositionComplete(); + + if(disp >= mDisplayDevices.size()){ + ETRACE("no device found"); + return false; + } + + IDisplayDevice *device = mDisplayDevices.itemAt(disp); + if (!device) { + ETRACE("no device found"); + return false; + } + + return device->compositionComplete(); +} + +void Hwcomposer::vsync(int disp, int64_t timestamp) +{ + RETURN_VOID_IF_NOT_INIT(); + + if (mProcs && mProcs->vsync) { + VTRACE("report vsync on disp %d, timestamp %llu", disp, timestamp); + // workaround to pretend vsync is from primary display + // Display will freeze if vsync is from external display. + mProcs->vsync(const_cast<hwc_procs_t*>(mProcs), IDisplayDevice::DEVICE_PRIMARY, timestamp); + } +} + +void Hwcomposer::hotplug(int disp, bool connected) +{ + RETURN_VOID_IF_NOT_INIT(); + + // TODO: Two fake hotplug events are sent during mode setting. To avoid + // unnecessary audio switch, real connection status should be sent to MDS + mMultiDisplayObserver->notifyHotPlug(mDrm->isConnected(disp)); + + if (mProcs && mProcs->hotplug) { + DTRACE("report hotplug on disp %d, connected %d", disp, connected); + mProcs->hotplug(const_cast<hwc_procs_t*>(mProcs), disp, connected); + DTRACE("hotplug callback processed and returned!"); + } + + mDisplayAnalyzer->postHotplugEvent(connected); +} + +void Hwcomposer::invalidate() +{ + RETURN_VOID_IF_NOT_INIT(); + + if (mProcs && mProcs->invalidate) { + DTRACE("invalidating screen..."); + mProcs->invalidate(const_cast<hwc_procs_t*>(mProcs)); + } +} + +bool Hwcomposer::release() +{ + RETURN_FALSE_IF_NOT_INIT(); + + return true; +} + +bool Hwcomposer::dump(char *buff, int buff_len, int *cur_len) +{ + RETURN_FALSE_IF_NOT_INIT(); + + Dump d(buff, buff_len); + + // dump composer status + d.append("Hardware Composer state:"); + // dump device status + for (size_t i= 0; i < mDisplayDevices.size(); i++) { + IDisplayDevice *device = mDisplayDevices.itemAt(i); + if (device) + device->dump(d); + } + + // dump plane manager status + if (mPlaneManager) + mPlaneManager->dump(d); + + // dump buffer manager status + if (mBufferManager) + mBufferManager->dump(d); + + return true; +} + +void Hwcomposer::registerProcs(hwc_procs_t const *procs) +{ + CTRACE(); + + if (!procs) { + WTRACE("procs is NULL"); + } + mProcs = procs; +} + +bool Hwcomposer::initialize() +{ + CTRACE(); + + // create drm + mDrm = new Drm(); + if (!mDrm || !mDrm->initialize()) { + DEINIT_AND_RETURN_FALSE("failed to create DRM"); + } + + if (!mPlatFactory){ + DEINIT_AND_RETURN_FALSE("failed to provide a PlatFactory"); + } + + // create buffer manager + mBufferManager = mPlatFactory->createBufferManager(); + if (!mBufferManager || !mBufferManager->initialize()) { + DEINIT_AND_RETURN_FALSE("failed to create buffer manager"); + } + + // create display plane manager + mPlaneManager = mPlatFactory->createDisplayPlaneManager(); + if (!mPlaneManager || !mPlaneManager->initialize()) { + DEINIT_AND_RETURN_FALSE("failed to create display plane manager"); + } + + mDisplayContext = mPlatFactory->createDisplayContext(); + if (!mDisplayContext || !mDisplayContext->initialize()) { + DEINIT_AND_RETURN_FALSE("failed to create display context"); + } + + mUeventObserver = new UeventObserver(); + if (!mUeventObserver || !mUeventObserver->initialize()) { + DEINIT_AND_RETURN_FALSE("failed to initialize uevent observer"); + } + + // create display device + mDisplayDevices.clear(); + for (int i = 0; i < IDisplayDevice::DEVICE_COUNT; i++) { + IDisplayDevice *device = mPlatFactory->createDisplayDevice(i); + if (!device || !device->initialize()) { + DEINIT_AND_DELETE_OBJ(device); + DEINIT_AND_RETURN_FALSE("failed to create device %d", i); + } + // add this device + ETRACE("HWC devices initialize device is %p at %d", device, i); + mDisplayDevices.insertAt(device, i, 1); + } + + mVsyncManager = new VsyncManager(*this); + if (!mVsyncManager || !mVsyncManager->initialize()) { + DEINIT_AND_RETURN_FALSE("failed to create Vsync Manager"); + } + + mDisplayAnalyzer = new DisplayAnalyzer(); + if (!mDisplayAnalyzer || !mDisplayAnalyzer->initialize()) { + DEINIT_AND_RETURN_FALSE("failed to initialize display analyzer"); + } + + mMultiDisplayObserver = new MultiDisplayObserver(); + if (!mMultiDisplayObserver || !mMultiDisplayObserver->initialize()) { + DEINIT_AND_RETURN_FALSE("failed to initialize display observer"); + } + + // all initialized, starting uevent observer + mUeventObserver->start(); + + mInitialized = true; + return true; +} + +void Hwcomposer::deinitialize() +{ + DEINIT_AND_DELETE_OBJ(mMultiDisplayObserver); + DEINIT_AND_DELETE_OBJ(mDisplayAnalyzer); + // delete mVsyncManager first as it holds reference to display devices. + DEINIT_AND_DELETE_OBJ(mVsyncManager); + + DEINIT_AND_DELETE_OBJ(mUeventObserver); + // destroy display devices + for (size_t i = 0; i < mDisplayDevices.size(); i++) { + IDisplayDevice *device = mDisplayDevices.itemAt(i); + DEINIT_AND_DELETE_OBJ(device); + } + mDisplayDevices.clear(); + + if (mPlatFactory) { + delete mPlatFactory; + mPlatFactory = 0; + } + + DEINIT_AND_DELETE_OBJ(mDisplayContext); + DEINIT_AND_DELETE_OBJ(mPlaneManager); + DEINIT_AND_DELETE_OBJ(mBufferManager); + DEINIT_AND_DELETE_OBJ(mDrm); + mInitialized = false; +} + +Drm* Hwcomposer::getDrm() +{ + return mDrm; +} + +DisplayPlaneManager* Hwcomposer::getPlaneManager() +{ + return mPlaneManager; +} + +BufferManager* Hwcomposer::getBufferManager() +{ + return mBufferManager; +} + +IDisplayContext* Hwcomposer::getDisplayContext() +{ + return mDisplayContext; +} + +DisplayAnalyzer* Hwcomposer::getDisplayAnalyzer() +{ + return mDisplayAnalyzer; +} + +MultiDisplayObserver* Hwcomposer::getMultiDisplayObserver() +{ + return mMultiDisplayObserver; +} + +IDisplayDevice* Hwcomposer::getDisplayDevice(int disp) +{ + if (disp < 0 || disp >= IDisplayDevice::DEVICE_COUNT) { + ETRACE("invalid disp %d", disp); + return NULL; + } + return mDisplayDevices.itemAt(disp); +} + +VsyncManager* Hwcomposer::getVsyncManager() +{ + return mVsyncManager; +} + +UeventObserver* Hwcomposer::getUeventObserver() +{ + return mUeventObserver; +} + +} // namespace intel +} // namespace android diff --git a/merrifield/common/base/SimpleThread.h b/merrifield/common/base/SimpleThread.h new file mode 100644 index 0000000..ade0e84 --- /dev/null +++ b/merrifield/common/base/SimpleThread.h @@ -0,0 +1,38 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef SIMPLE_THREAD_H +#define SIMPLE_THREAD_H + +#include <utils/threads.h> + +#define DECLARE_THREAD(THREADNAME, THREADOWNER) \ + class THREADNAME: public Thread { \ + public: \ + THREADNAME(THREADOWNER *owner) { mOwner = owner; } \ + THREADNAME() { mOwner = NULL; } \ + private: \ + virtual bool threadLoop() { return mOwner->threadLoop(); } \ + private: \ + THREADOWNER *mOwner; \ + }; \ + friend class THREADNAME; \ + bool threadLoop(); \ + sp<THREADNAME> mThread; + + +#endif /* SIMPLE_THREAD_H */ + diff --git a/merrifield/common/base/VsyncManager.cpp b/merrifield/common/base/VsyncManager.cpp new file mode 100644 index 0000000..56e935e --- /dev/null +++ b/merrifield/common/base/VsyncManager.cpp @@ -0,0 +1,218 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <HwcTrace.h> +#include <IDisplayDevice.h> +#include <DisplayQuery.h> +#include <BufferManager.h> +#include <DisplayPlaneManager.h> +#include <Hwcomposer.h> +#include <VsyncManager.h> + + +namespace android { +namespace intel { + +VsyncManager::VsyncManager(Hwcomposer &hwc) + :mHwc(hwc), + mInitialized(false), + mEnableDynamicVsync(true), + mEnabled(false), + mVsyncSource(IDisplayDevice::DEVICE_COUNT), + mLock() +{ +} + +VsyncManager::~VsyncManager() +{ + WARN_IF_NOT_DEINIT(); +} + +bool VsyncManager::initialize() +{ + + mEnabled = false; + mVsyncSource = IDisplayDevice::DEVICE_COUNT; + mEnableDynamicVsync = !scUsePrimaryVsyncOnly; + mInitialized = true; + return true; +} + +void VsyncManager::deinitialize() +{ + if (mEnabled) { + WTRACE("vsync is still enabled"); + } + + mVsyncSource = IDisplayDevice::DEVICE_COUNT; + mEnabled = false; + mEnableDynamicVsync = !scUsePrimaryVsyncOnly; + mInitialized = false; +} + +bool VsyncManager::handleVsyncControl(int disp, bool enabled) +{ + Mutex::Autolock l(mLock); + + if (disp != IDisplayDevice::DEVICE_PRIMARY) { + WTRACE("vsync control on non-primary device %d", disp); + return false; + } + + if (mEnabled == enabled) { + WTRACE("vsync state %d is not changed", enabled); + return true; + } + + if (!enabled) { + disableVsync(); + mEnabled = false; + return true; + } else { + mEnabled = enableVsync(getCandidate()); + return mEnabled; + } + + return false; +} + +void VsyncManager::resetVsyncSource() +{ + Mutex::Autolock l(mLock); + + if (!mEnableDynamicVsync) { + ITRACE("dynamic vsync source switch is not supported"); + return; + } + + if (!mEnabled) { + return; + } + + int vsyncSource = getCandidate(); + if (vsyncSource == mVsyncSource) { + return; + } + + disableVsync(); + enableVsync(vsyncSource); +} + +int VsyncManager::getVsyncSource() +{ + return mVsyncSource; +} + +void VsyncManager::enableDynamicVsync(bool enable) +{ + Mutex::Autolock l(mLock); + if (scUsePrimaryVsyncOnly) { + WTRACE("dynamic vsync is not supported"); + return; + } + + mEnableDynamicVsync = enable; + + if (!mEnabled) { + return; + } + + int vsyncSource = getCandidate(); + if (vsyncSource == mVsyncSource) { + return; + } + + disableVsync(); + enableVsync(vsyncSource); +} + +IDisplayDevice* VsyncManager::getDisplayDevice(int dispType ) { + return mHwc.getDisplayDevice(dispType); +} + +int VsyncManager::getCandidate() +{ + if (!mEnableDynamicVsync) { + return IDisplayDevice::DEVICE_PRIMARY; + } + + IDisplayDevice *device = NULL; + // use HDMI vsync when connected + device = getDisplayDevice(IDisplayDevice::DEVICE_EXTERNAL); + if (device && device->isConnected()) { + return IDisplayDevice::DEVICE_EXTERNAL; + } + +#ifdef INTEL_WIDI_MERRIFIELD + // use vsync from virtual display when video extended mode is entered + if (Hwcomposer::getInstance().getDisplayAnalyzer()->isVideoExtModeActive()) { + device = getDisplayDevice(IDisplayDevice::DEVICE_VIRTUAL); + if (device && device->isConnected()) { + return IDisplayDevice::DEVICE_VIRTUAL; + } + WTRACE("Could not use vsync from secondary device"); + } +#endif + return IDisplayDevice::DEVICE_PRIMARY; +} + +bool VsyncManager::enableVsync(int candidate) +{ + if (mVsyncSource != IDisplayDevice::DEVICE_COUNT) { + WTRACE("vsync has been enabled on %d", mVsyncSource); + return true; + } + + IDisplayDevice *device = getDisplayDevice(candidate); + if (!device) { + ETRACE("invalid vsync source candidate %d", candidate); + return false; + } + + if (device->vsyncControl(true)) { + mVsyncSource = candidate; + return true; + } + + if (candidate != IDisplayDevice::DEVICE_PRIMARY) { + WTRACE("failed to enable vsync on display %d, fall back to primary", candidate); + device = getDisplayDevice(IDisplayDevice::DEVICE_PRIMARY); + if (device && device->vsyncControl(true)) { + mVsyncSource = IDisplayDevice::DEVICE_PRIMARY; + return true; + } + } + ETRACE("failed to enable vsync on the primary display"); + return false; +} + +void VsyncManager::disableVsync() +{ + if (mVsyncSource == IDisplayDevice::DEVICE_COUNT) { + WTRACE("vsync has been disabled"); + return; + } + + IDisplayDevice *device = getDisplayDevice(mVsyncSource); + if (device && !device->vsyncControl(false)) { + WTRACE("failed to disable vsync on device %d", mVsyncSource); + } + mVsyncSource = IDisplayDevice::DEVICE_COUNT; +} + +} // namespace intel +} // namespace android + diff --git a/merrifield/common/base/VsyncManager.h b/merrifield/common/base/VsyncManager.h new file mode 100644 index 0000000..4af10ed --- /dev/null +++ b/merrifield/common/base/VsyncManager.h @@ -0,0 +1,65 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef VSYNC_MANAGER_H +#define VSYNC_MANAGER_H + +#include <IDisplayDevice.h> +#include <utils/threads.h> + +namespace android { +namespace intel { + + +class Hwcomposer; + +class VsyncManager { +public: + VsyncManager(Hwcomposer& hwc); + virtual ~VsyncManager(); + +public: + bool initialize(); + void deinitialize(); + bool handleVsyncControl(int disp, bool enabled); + void resetVsyncSource(); + int getVsyncSource(); + void enableDynamicVsync(bool enable); + +private: + inline int getCandidate(); + inline bool enableVsync(int candidate); + inline void disableVsync(); + IDisplayDevice* getDisplayDevice(int dispType); + +private: + Hwcomposer &mHwc; + bool mInitialized; + bool mEnableDynamicVsync; + bool mEnabled; + int mVsyncSource; + Mutex mLock; + +private: + // toggle this constant to use primary vsync only or enable dynamic vsync. + static const bool scUsePrimaryVsyncOnly = false; +}; + +} // namespace intel +} // namespace android + + + +#endif /* VSYNC_MANAGER_H */ diff --git a/merrifield/common/buffers/BufferCache.cpp b/merrifield/common/buffers/BufferCache.cpp new file mode 100644 index 0000000..cfd14d3 --- /dev/null +++ b/merrifield/common/buffers/BufferCache.cpp @@ -0,0 +1,97 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <HwcTrace.h> +#include <BufferCache.h> + +namespace android { +namespace intel { + +BufferCache::BufferCache(int size) +{ + mBufferPool.setCapacity(size); +} + +BufferCache::~BufferCache() +{ + if (mBufferPool.size() != 0) { + ETRACE("buffer cache is not empty"); + } + mBufferPool.clear(); +} + +bool BufferCache::addMapper(uint64_t handle, BufferMapper* mapper) +{ + ssize_t index = mBufferPool.indexOfKey(handle); + if (index >= 0) { + ETRACE("buffer %#llx exists", handle); + return false; + } + + // add mapper + index = mBufferPool.add(handle, mapper); + if (index < 0) { + ETRACE("failed to add mapper. err = %d", (int)index); + return false; + } + + return true; +} + +bool BufferCache::removeMapper(BufferMapper* mapper) +{ + ssize_t index; + + if (!mapper) { + ETRACE("invalid mapper"); + return false; + } + + index = mBufferPool.removeItem(mapper->getKey()); + if (index < 0) { + WTRACE("failed to remove mapper. err = %d", (int)index); + return false; + } + + return true; +} + +BufferMapper* BufferCache::getMapper(uint64_t handle) +{ + ssize_t index = mBufferPool.indexOfKey(handle); + if (index < 0) { + // don't add ETRACE here as this condition will happen frequently + return 0; + } + return mBufferPool.valueAt(index); +} + +size_t BufferCache::getCacheSize() const +{ + return mBufferPool.size(); +} + +BufferMapper* BufferCache::getMapper(uint32_t index) +{ + if (index >= mBufferPool.size()) { + ETRACE("invalid index"); + return 0; + } + BufferMapper* mapper = mBufferPool.valueAt(index); + return mapper; +} + +} // namespace intel +} // namespace android diff --git a/merrifield/common/buffers/BufferCache.h b/merrifield/common/buffers/BufferCache.h new file mode 100644 index 0000000..fc1ab86 --- /dev/null +++ b/merrifield/common/buffers/BufferCache.h @@ -0,0 +1,48 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef BUFFERCACHE_H_ +#define BUFFERCACHE_H_ + +#include <utils/KeyedVector.h> +#include <BufferMapper.h> + +namespace android { +namespace intel { + +// Generic buffer cache +class BufferCache { +public: + BufferCache(int size); + virtual ~BufferCache(); + // add a new mapper into buffer cache + virtual bool addMapper(uint64_t handle, BufferMapper* mapper); + //remove mapper + virtual bool removeMapper(BufferMapper* mapper); + // get a buffer mapper + virtual BufferMapper* getMapper(uint64_t handle); + // get cache size + virtual size_t getCacheSize() const; + // get mapper with an index + virtual BufferMapper* getMapper(uint32_t index); +private: + KeyedVector<uint64_t, BufferMapper*> mBufferPool; +}; + +} +} + + +#endif /* BUFFERCACHE_H_ */ diff --git a/merrifield/common/buffers/BufferManager.cpp b/merrifield/common/buffers/BufferManager.cpp new file mode 100644 index 0000000..fc460b3 --- /dev/null +++ b/merrifield/common/buffers/BufferManager.cpp @@ -0,0 +1,358 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <HwcTrace.h> +#include <hardware/hwcomposer.h> +#include <BufferManager.h> +#include <DrmConfig.h> + +namespace android { +namespace intel { + +BufferManager::BufferManager() + : mGrallocModule(NULL), + mAllocDev(NULL), + mFrameBuffers(), + mBufferPool(NULL), + mDataBuffer(NULL), + mDataBufferLock(), + mInitialized(false) +{ + CTRACE(); +} + +BufferManager::~BufferManager() +{ + WARN_IF_NOT_DEINIT(); +} + +bool BufferManager::initCheck() const +{ + return mInitialized; +} + +bool BufferManager::initialize() +{ + CTRACE(); + + // create buffer pool + mBufferPool = new BufferCache(DEFAULT_BUFFER_POOL_SIZE); + if (!mBufferPool) { + ETRACE("failed to create gralloc buffer cache"); + return false; + } + + // init gralloc module + hw_module_t const* module; + if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module)) { + DEINIT_AND_RETURN_FALSE("failed to get gralloc module"); + } + mGrallocModule = (gralloc_module_t*)module; + + gralloc_open(module, &mAllocDev); + if (!mAllocDev) { + WTRACE("failed to open alloc device"); + } + + // create a dummy data buffer + mDataBuffer = createDataBuffer(mGrallocModule, 0); + if (!mDataBuffer) { + DEINIT_AND_RETURN_FALSE("failed to create data buffer"); + } + + mInitialized = true; + return true; +} + +void BufferManager::deinitialize() +{ + mInitialized = false; + + if (mBufferPool) { + // unmap & delete all cached buffer mappers + for (size_t i = 0; i < mBufferPool->getCacheSize(); i++) { + BufferMapper *mapper = mBufferPool->getMapper(i); + mapper->unmap(); + delete mapper; + } + + delete mBufferPool; + mBufferPool = NULL; + } + + for (size_t j = 0; j < mFrameBuffers.size(); j++) { + BufferMapper *mapper = mFrameBuffers.valueAt(j); + mapper->unmap(); + delete mapper; + } + mFrameBuffers.clear(); + + if (mAllocDev) { + gralloc_close(mAllocDev); + mAllocDev = NULL; + } + + if (mDataBuffer) { + delete mDataBuffer; + mDataBuffer = NULL; + } +} + +void BufferManager::dump(Dump& d) +{ + d.append("Buffer Manager status: pool size %d\n", mBufferPool->getCacheSize()); + d.append("-------------------------------------------------------------\n"); + for (uint32_t i = 0; i < mBufferPool->getCacheSize(); i++) { + BufferMapper *mapper = mBufferPool->getMapper(i); + d.append("Buffer %d: handle %#x, (%dx%d), format %d, refCount %d\n", + i, + mapper->getHandle(), + mapper->getWidth(), + mapper->getHeight(), + mapper->getFormat(), + mapper->getRef()); + } + return; +} + +DataBuffer* BufferManager::lockDataBuffer(buffer_handle_t handle) +{ + mDataBufferLock.lock(); + mDataBuffer->resetBuffer(handle); + return mDataBuffer; +} + +void BufferManager::unlockDataBuffer(DataBuffer *buffer) +{ + mDataBufferLock.unlock(); +} + +DataBuffer* BufferManager::get(buffer_handle_t handle) +{ + return createDataBuffer(mGrallocModule, handle); +} + +void BufferManager::put(DataBuffer *buffer) +{ + delete buffer; +} + +BufferMapper* BufferManager::map(DataBuffer& buffer) +{ + bool ret; + BufferMapper* mapper; + + CTRACE(); + Mutex::Autolock _l(mLock); + //try to get mapper from pool + mapper = mBufferPool->getMapper(buffer.getKey()); + if (mapper) { + // increase mapper ref count + mapper->incRef(); + return mapper; + } + + // create a new buffer mapper and add it to pool + do { + VTRACE("new buffer, will add it"); + mapper = createBufferMapper(mGrallocModule, buffer); + if (!mapper) { + ETRACE("failed to allocate mapper"); + break; + } + ret = mapper->map(); + if (!ret) { + ETRACE("failed to map"); + delete mapper; + mapper = NULL; + break; + } + ret = mBufferPool->addMapper(buffer.getKey(), mapper); + if (!ret) { + ETRACE("failed to add mapper"); + break; + } + // increase mapper ref count + mapper->incRef(); + return mapper; + } while (0); + + // error handling + if (mapper) { + mapper->unmap(); + delete mapper; + } + return NULL; +} + +void BufferManager::unmap(BufferMapper *mapper) +{ + Mutex::Autolock _l(mLock); + if (!mapper) { + ETRACE("invalid mapper"); + return; + } + + // unmap & remove this mapper from buffer when refCount = 0 + int refCount = mapper->decRef(); + if (refCount < 0) { + ETRACE("invalid ref count"); + } else if (!refCount) { + // remove mapper from buffer pool + mBufferPool->removeMapper(mapper); + mapper->unmap(); + delete mapper; + } +} + +buffer_handle_t BufferManager::allocFrameBuffer(int width, int height, int *stride) +{ + RETURN_NULL_IF_NOT_INIT(); + + if (!mAllocDev) { + WTRACE("Alloc device is not available"); + return 0; + } + + if (!width || !height || !stride) { + ETRACE("invalid input parameter"); + return 0; + } + + ITRACE("size of frame buffer to create: %dx%d", width, height); + buffer_handle_t handle = 0; + status_t err = mAllocDev->alloc( + mAllocDev, + width, + height, + DrmConfig::getFrameBufferFormat(), + 0, // GRALLOC_USAGE_HW_FB + &handle, + stride); + + if (err != 0) { + ETRACE("failed to allocate frame buffer, error = %d", err); + return 0; + } + + DataBuffer *buffer = NULL; + BufferMapper *mapper = NULL; + + do { + buffer = lockDataBuffer(handle); + if (!buffer) { + ETRACE("failed to get data buffer, handle = %p", handle); + break; + } + + mapper = createBufferMapper(mGrallocModule, *buffer); + if (!mapper) { + ETRACE("failed to create buffer mapper"); + break; + } + + buffer_handle_t fbHandle; + if (!(fbHandle = mapper->getFbHandle(0))) { + ETRACE("failed to get Fb handle"); + break; + } + + mFrameBuffers.add(fbHandle, mapper); + unlockDataBuffer(buffer); + return fbHandle; + } while (0); + + // error handling, release all allocated resources + if (buffer) { + unlockDataBuffer(buffer); + } + if (mapper) { + delete mapper; + } + mAllocDev->free(mAllocDev, handle); + return 0; +} + +void BufferManager::freeFrameBuffer(buffer_handle_t fbHandle) +{ + RETURN_VOID_IF_NOT_INIT(); + + if (!mAllocDev) { + WTRACE("Alloc device is not available"); + return; + } + + ssize_t index = mFrameBuffers.indexOfKey(fbHandle); + if (index < 0) { + ETRACE("invalid kernel handle"); + return; + } + + BufferMapper *mapper = mFrameBuffers.valueAt(index); + buffer_handle_t handle = mapper->getHandle(); + mapper->putFbHandle(); + delete mapper; + mFrameBuffers.removeItem(fbHandle); + mAllocDev->free(mAllocDev, handle); +} + +buffer_handle_t BufferManager::allocGrallocBuffer(uint32_t width, uint32_t height, uint32_t format, uint32_t usage) +{ + RETURN_NULL_IF_NOT_INIT(); + + if (!mAllocDev) { + WTRACE("Alloc device is not available"); + return 0; + } + + if (!width || !height) { + ETRACE("invalid input parameter"); + return 0; + } + + ITRACE("size of graphic buffer to create: %dx%d", width, height); + buffer_handle_t handle = 0; + int stride; + status_t err = mAllocDev->alloc( + mAllocDev, + width, + height, + format, + usage, + &handle, + &stride); + if (err != 0) { + ETRACE("failed to allocate gralloc buffer, error = %d", err); + return 0; + } + + return handle; +} + +void BufferManager::freeGrallocBuffer(buffer_handle_t handle) +{ + RETURN_VOID_IF_NOT_INIT(); + if (!mAllocDev) { + WTRACE("Alloc device is not available"); + return; + } + + if (handle) + mAllocDev->free(mAllocDev, handle); +} + +} // namespace intel +} // namespace android diff --git a/merrifield/common/buffers/GraphicBuffer.cpp b/merrifield/common/buffers/GraphicBuffer.cpp new file mode 100644 index 0000000..59e8766 --- /dev/null +++ b/merrifield/common/buffers/GraphicBuffer.cpp @@ -0,0 +1,77 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <HwcTrace.h> +#include <GraphicBuffer.h> + +namespace android { +namespace intel { + +GraphicBuffer::GraphicBuffer(buffer_handle_t handle) + : DataBuffer(handle) +{ + initBuffer(handle); +} + +void GraphicBuffer::resetBuffer(buffer_handle_t handle) +{ + DataBuffer::resetBuffer(handle); + initBuffer(handle); +} + +bool GraphicBuffer::isProtectedUsage(uint32_t usage) +{ + if (usage == USAGE_INVALID) { + return false; + } + + return (usage & GRALLOC_USAGE_PROTECTED) != 0; +} + +bool GraphicBuffer::isProtectedBuffer(GraphicBuffer *buffer) +{ + if (buffer == NULL) { + return false; + } + + return isProtectedUsage(buffer->mUsage); +} + +bool GraphicBuffer::isCompressionUsage(uint32_t usage) +{ + if (usage == USAGE_INVALID) { + return false; + } + + return false; +} + +bool GraphicBuffer::isCompressionBuffer(GraphicBuffer *buffer) +{ + if (buffer == NULL) { + return false; + } + + return isCompressionUsage(buffer->mUsage); +} + +void GraphicBuffer::initBuffer(buffer_handle_t handle) +{ + mUsage = USAGE_INVALID; + mBpp = 0; +} + +} +} diff --git a/merrifield/common/devices/ExternalDevice.cpp b/merrifield/common/devices/ExternalDevice.cpp new file mode 100644 index 0000000..d3de323 --- /dev/null +++ b/merrifield/common/devices/ExternalDevice.cpp @@ -0,0 +1,339 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <HwcTrace.h> +#include <Drm.h> +#include <DrmConfig.h> +#include <Hwcomposer.h> +#include <ExternalDevice.h> + +namespace android { +namespace intel { + +ExternalDevice::ExternalDevice(Hwcomposer& hwc, DeviceControlFactory* controlFactory) + : PhysicalDevice(DEVICE_EXTERNAL, hwc, controlFactory), + mHdcpControl(NULL), + mAbortModeSettingCond(), + mPendingDrmMode(), + mHotplugEventPending(false), + mExpectedRefreshRate(0) +{ + CTRACE(); +} + +ExternalDevice::~ExternalDevice() +{ + CTRACE(); +} + +bool ExternalDevice::initialize() +{ + if (!PhysicalDevice::initialize()) { + DEINIT_AND_RETURN_FALSE("failed to initialize physical device"); + } + + mHdcpControl = mControlFactory->createHdcpControl(); + if (!mHdcpControl) { + DEINIT_AND_RETURN_FALSE("failed to create HDCP control"); + } + + mHotplugEventPending = false; + if (mConnected) { + mHdcpControl->startHdcpAsync(HdcpLinkStatusListener, this); + } + + UeventObserver *observer = Hwcomposer::getInstance().getUeventObserver(); + if (observer) { + observer->registerListener( + DrmConfig::getHotplugString(), + hotplugEventListener, + this); + } else { + ETRACE("Uevent observer is NULL"); + } + return true; +} + +void ExternalDevice::deinitialize() +{ + // abort mode settings if it is in the middle + mAbortModeSettingCond.signal(); + if (mThread.get()) { + mThread->join(); + mThread = NULL; + } + + if (mHdcpControl) { + mHdcpControl->stopHdcp(); + delete mHdcpControl; + mHdcpControl = 0; + } + + mHotplugEventPending = false; + PhysicalDevice::deinitialize(); +} + +bool ExternalDevice::setDrmMode(drmModeModeInfo& value) +{ + if (!mConnected) { + WTRACE("external device is not connected"); + return false; + } + + if (mThread.get()) { + mThread->join(); + mThread = NULL; + } + + Drm *drm = Hwcomposer::getInstance().getDrm(); + drmModeModeInfo mode; + drm->getModeInfo(mType, mode); + if (drm->isSameDrmMode(&value, &mode)) + return true; + + // any issue here by faking connection status? + mConnected = false; + mPendingDrmMode = value; + + // setting mode in a working thread + mThread = new ModeSettingThread(this); + if (!mThread.get()) { + ETRACE("failed to create mode settings thread"); + return false; + } + + mThread->run("ModeSettingsThread", PRIORITY_URGENT_DISPLAY); + return true; +} + +bool ExternalDevice::threadLoop() +{ + // one-time execution + setDrmMode(); + return false; +} + +void ExternalDevice::setDrmMode() +{ + ITRACE("start mode setting..."); + + Drm *drm = Hwcomposer::getInstance().getDrm(); + + mConnected = false; + mHwc.hotplug(mType, false); + + { + Mutex::Autolock lock(mLock); + // TODO: make timeout value flexible, or wait until surface flinger + // acknowledges hot unplug event. + status_t err = mAbortModeSettingCond.waitRelative(mLock, milliseconds(20)); + if (err != -ETIMEDOUT) { + ITRACE("Mode settings is interrupted"); + mHwc.hotplug(mType, true); + return; + } + } + + // TODO: potential threading issue with onHotplug callback + mHdcpControl->stopHdcp(); + if (!drm->setDrmMode(mType, mPendingDrmMode)) { + ETRACE("failed to set Drm mode"); + mHwc.hotplug(mType, true); + return; + } + + if (!PhysicalDevice::updateDisplayConfigs()) { + ETRACE("failed to update display configs"); + mHwc.hotplug(mType, true); + return; + } + mConnected = true; + mHotplugEventPending = true; + // delay sending hotplug event until HDCP is authenticated + if (mHdcpControl->startHdcpAsync(HdcpLinkStatusListener, this) == false) { + ETRACE("startHdcpAsync() failed; HDCP is not enabled"); + mHotplugEventPending = false; + mHwc.hotplug(mType, true); + } + mExpectedRefreshRate = 0; +} + + +void ExternalDevice::HdcpLinkStatusListener(bool success, void *userData) +{ + if (userData == NULL) { + return; + } + + ExternalDevice *p = (ExternalDevice*)userData; + p->HdcpLinkStatusListener(success); +} + +void ExternalDevice::HdcpLinkStatusListener(bool success) +{ + if (!success) { + ETRACE("HDCP is not authenticated, disabling dynamic vsync"); + mHwc.getVsyncManager()->enableDynamicVsync(false); + } + + if (mHotplugEventPending) { + DTRACE("HDCP authentication status %d, sending hotplug event...", success); + mHwc.hotplug(mType, mConnected); + mHotplugEventPending = false; + } + + if (success) { + ITRACE("HDCP authenticated, enabling dynamic vsync"); + mHwc.getVsyncManager()->enableDynamicVsync(true); + } +} + +void ExternalDevice::hotplugEventListener(void *data) +{ + ExternalDevice *pThis = (ExternalDevice*)data; + if (pThis) { + pThis->hotplugListener(); + } +} + +void ExternalDevice::hotplugListener() +{ + bool ret; + + CTRACE(); + + // abort mode settings if it is in the middle + mAbortModeSettingCond.signal(); + + // remember the current connection status before detection + bool connected = mConnected; + + // detect display configs + ret = detectDisplayConfigs(); + if (ret == false) { + ETRACE("failed to detect display config"); + return; + } + + ITRACE("hotpug event: %d", mConnected); + + if (connected == mConnected) { + WTRACE("same connection status detected, hotplug event ignored"); + return; + } + + if (mConnected == false) { + mHotplugEventPending = false; + mHwc.getVsyncManager()->resetVsyncSource(); + mHdcpControl->stopHdcp(); + mHwc.hotplug(mType, mConnected); + } else { + DTRACE("start HDCP asynchronously..."); + // delay sending hotplug event till HDCP is authenticated. + mHotplugEventPending = true; + ret = mHdcpControl->startHdcpAsync(HdcpLinkStatusListener, this); + if (ret == false) { + ETRACE("failed to start HDCP"); + mHotplugEventPending = false; + mHwc.hotplug(mType, mConnected); + } + } + mActiveDisplayConfig = 0; +} + +int ExternalDevice::getRefreshRate() +{ + Drm *drm = Hwcomposer::getInstance().getDrm(); + drmModeModeInfo mode; + if (!drm->getModeInfo(IDisplayDevice::DEVICE_EXTERNAL, mode)) + return 0; + return mode.vrefresh; +} + +void ExternalDevice::setRefreshRate(int hz) +{ + RETURN_VOID_IF_NOT_INIT(); + + ITRACE("setting refresh rate to %d", hz); + + if (mBlank) { + WTRACE("external device is blank"); + return; + } + + Drm *drm = Hwcomposer::getInstance().getDrm(); + drmModeModeInfo mode; + if (!drm->getModeInfo(IDisplayDevice::DEVICE_EXTERNAL, mode)) + return; + + if (hz == 0 && (mode.type & DRM_MODE_TYPE_PREFERRED)) + return; + + if (hz == (int)mode.vrefresh) + return; + + if (mExpectedRefreshRate != 0 && + mExpectedRefreshRate == hz && mHotplugEventPending) { + ITRACE("Ignore a new refresh setting event because there is a same event is handling"); + return; + } + mExpectedRefreshRate = hz; + + ITRACE("changing refresh rate from %d to %d", mode.vrefresh, hz); + + mHwc.getVsyncManager()->enableDynamicVsync(false); + + mHdcpControl->stopHdcp(); + + drm->setRefreshRate(IDisplayDevice::DEVICE_EXTERNAL, hz); + + mHotplugEventPending = false; + mHdcpControl->startHdcpAsync(HdcpLinkStatusListener, this); + mHwc.getVsyncManager()->enableDynamicVsync(true); +} + +int ExternalDevice::getActiveConfig() +{ + if (!mConnected) { + return 0; + } + return mActiveDisplayConfig; +} + +bool ExternalDevice::setActiveConfig(int index) +{ + if (!mConnected) { + if (index == 0) + return true; + else + return false; + } + + // for now we will only permit the frequency change. In the future + // we may need to set mode as well. + if (index >= 0 && index < static_cast<int>(mDisplayConfigs.size())) { + DisplayConfig *config = mDisplayConfigs.itemAt(index); + setRefreshRate(config->getRefreshRate()); + mActiveDisplayConfig = index; + return true; + } else { + return false; + } + return true; +} + +} // namespace intel +} // namespace android diff --git a/merrifield/common/devices/PhysicalDevice.cpp b/merrifield/common/devices/PhysicalDevice.cpp new file mode 100644 index 0000000..a7e52cd --- /dev/null +++ b/merrifield/common/devices/PhysicalDevice.cpp @@ -0,0 +1,542 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <HwcTrace.h> +#include <Hwcomposer.h> +#include <Drm.h> +#include <PhysicalDevice.h> + +namespace android { +namespace intel { + +PhysicalDevice::PhysicalDevice(uint32_t type, Hwcomposer& hwc, DeviceControlFactory* controlFactory) + : mType(type), + mHwc(hwc), + mActiveDisplayConfig(-1), + mBlankControl(NULL), + mVsyncObserver(NULL), + mControlFactory(controlFactory), + mLayerList(NULL), + mConnected(false), + mBlank(false), + mDisplayState(DEVICE_DISPLAY_ON), + mInitialized(false) +{ + CTRACE(); + + switch (type) { + case DEVICE_PRIMARY: + mName = "Primary"; + break; + case DEVICE_EXTERNAL: + mName = "External"; + break; + default: + mName = "Unknown"; + } + + mDisplayConfigs.setCapacity(DEVICE_COUNT); +} + +PhysicalDevice::~PhysicalDevice() +{ + WARN_IF_NOT_DEINIT(); +} + +void PhysicalDevice::onGeometryChanged(hwc_display_contents_1_t *list) +{ + if (!list) { + ETRACE("list is NULL"); + return; + } + + ATRACE("disp = %d, layer number = %d", mType, list->numHwLayers); + + // NOTE: should NOT be here + if (mLayerList) { + WTRACE("mLayerList exists"); + DEINIT_AND_DELETE_OBJ(mLayerList); + } + + // create a new layer list + mLayerList = new HwcLayerList(list, mType); + if (!mLayerList) { + WTRACE("failed to create layer list"); + } +} + +bool PhysicalDevice::prePrepare(hwc_display_contents_1_t *display) +{ + RETURN_FALSE_IF_NOT_INIT(); + Mutex::Autolock _l(mLock); + + // for a null list, delete hwc list + if (!mConnected || !display || mBlank) { + if (mLayerList) { + DEINIT_AND_DELETE_OBJ(mLayerList); + } + return true; + } + + // check if geometry is changed, if changed delete list + if ((display->flags & HWC_GEOMETRY_CHANGED) && mLayerList) { + DEINIT_AND_DELETE_OBJ(mLayerList); + } + return true; +} + +bool PhysicalDevice::prepare(hwc_display_contents_1_t *display) +{ + RETURN_FALSE_IF_NOT_INIT(); + Mutex::Autolock _l(mLock); + + if (!mConnected || !display || mBlank) + return true; + + // check if geometry is changed + if (display->flags & HWC_GEOMETRY_CHANGED) { + onGeometryChanged(display); + } + if (!mLayerList) { + WTRACE("null HWC layer list"); + return true; + } + + // update list with new list + return mLayerList->update(display); +} + + +bool PhysicalDevice::commit(hwc_display_contents_1_t *display, IDisplayContext *context) +{ + RETURN_FALSE_IF_NOT_INIT(); + + if (!display || !context || !mLayerList || mBlank) { + return true; + } + return context->commitContents(display, mLayerList); +} + +bool PhysicalDevice::vsyncControl(bool enabled) +{ + RETURN_FALSE_IF_NOT_INIT(); + + ATRACE("disp = %d, enabled = %d", mType, enabled); + return mVsyncObserver->control(enabled); +} + +bool PhysicalDevice::blank(bool blank) +{ + RETURN_FALSE_IF_NOT_INIT(); + + if (!mConnected) + return false; + + mBlank = blank; + bool ret = mBlankControl->blank(mType, blank); + if (ret == false) { + ETRACE("failed to blank device"); + return false; + } + + return true; +} + +bool PhysicalDevice::getDisplaySize(int *width, int *height) +{ + RETURN_FALSE_IF_NOT_INIT(); + Mutex::Autolock _l(mLock); + if (!width || !height) { + ETRACE("invalid parameters"); + return false; + } + + *width = 0; + *height = 0; + drmModeModeInfo mode; + Drm *drm = Hwcomposer::getInstance().getDrm(); + bool ret = drm->getModeInfo(mType, mode); + if (!ret) { + return false; + } + + *width = mode.hdisplay; + *height = mode.vdisplay; + return true; +} + +template <typename T> +static inline T min(T a, T b) { + return a<b ? a : b; +} + +bool PhysicalDevice::getDisplayConfigs(uint32_t *configs, + size_t *numConfigs) +{ + RETURN_FALSE_IF_NOT_INIT(); + + Mutex::Autolock _l(mLock); + + if (!mConnected) { + ITRACE("device is not connected"); + return false; + } + + if (!configs || !numConfigs || *numConfigs < 1) { + ETRACE("invalid parameters"); + return false; + } + + // fill in all config handles + *numConfigs = min(*numConfigs, mDisplayConfigs.size()); + for (int i = 0; i < static_cast<int>(*numConfigs); i++) { + configs[i] = i; + } + + return true; +} +bool PhysicalDevice::getDisplayAttributes(uint32_t config, + const uint32_t *attributes, + int32_t *values) +{ + RETURN_FALSE_IF_NOT_INIT(); + + Mutex::Autolock _l(mLock); + + if (!mConnected) { + ITRACE("device is not connected"); + return false; + } + + if (!attributes || !values) { + ETRACE("invalid parameters"); + return false; + } + + DisplayConfig *configChosen = mDisplayConfigs.itemAt(config); + if (!configChosen) { + WTRACE("failed to get display config"); + return false; + } + + int i = 0; + while (attributes[i] != HWC_DISPLAY_NO_ATTRIBUTE) { + switch (attributes[i]) { + case HWC_DISPLAY_VSYNC_PERIOD: + if (configChosen->getRefreshRate()) { + values[i] = 1e9 / configChosen->getRefreshRate(); + } else { + ETRACE("refresh rate is 0!!!"); + values[i] = 0; + } + break; + case HWC_DISPLAY_WIDTH: + values[i] = configChosen->getWidth(); + break; + case HWC_DISPLAY_HEIGHT: + values[i] = configChosen->getHeight(); + break; + case HWC_DISPLAY_DPI_X: + values[i] = configChosen->getDpiX() * 1000.0f; + break; + case HWC_DISPLAY_DPI_Y: + values[i] = configChosen->getDpiY() * 1000.0f; + break; + default: + ETRACE("unknown attribute %d", attributes[i]); + break; + } + i++; + } + + return true; +} + +bool PhysicalDevice::compositionComplete() +{ + CTRACE(); + // do nothing by default + return true; +} + +void PhysicalDevice::removeDisplayConfigs() +{ + for (size_t i = 0; i < mDisplayConfigs.size(); i++) { + DisplayConfig *config = mDisplayConfigs.itemAt(i); + delete config; + } + + mDisplayConfigs.clear(); + mActiveDisplayConfig = -1; +} + +bool PhysicalDevice::detectDisplayConfigs() +{ + Mutex::Autolock _l(mLock); + + Drm *drm = Hwcomposer::getInstance().getDrm(); + if (!drm->detect(mType)) { + ETRACE("drm detection on device %d failed ", mType); + return false; + } + return updateDisplayConfigs(); +} + +bool PhysicalDevice::updateDisplayConfigs() +{ + bool ret; + Drm *drm = Hwcomposer::getInstance().getDrm(); + + // reset display configs + removeDisplayConfigs(); + + // update device connection status + mConnected = drm->isConnected(mType); + if (!mConnected) { + return true; + } + + // reset the number of display configs + mDisplayConfigs.setCapacity(1); + + drmModeModeInfo mode; + ret = drm->getModeInfo(mType, mode); + if (!ret) { + ETRACE("failed to get mode info"); + mConnected = false; + return false; + } + + uint32_t mmWidth, mmHeight; + ret = drm->getPhysicalSize(mType, mmWidth, mmHeight); + if (!ret) { + ETRACE("failed to get physical size"); + mConnected = false; + return false; + } + + float physWidthInch = (float)mmWidth * 0.039370f; + float physHeightInch = (float)mmHeight * 0.039370f; + + // use current drm mode, likely it's preferred mode + int dpiX = 0; + int dpiY = 0; + if (physWidthInch && physHeightInch) { + dpiX = mode.hdisplay / physWidthInch; + dpiY = mode.vdisplay / physHeightInch; + } else { + ETRACE("invalid physical size, EDID read error?"); + // don't bail out as it is not a fatal error + } + // use active fb dimension as config width/height + DisplayConfig *config = new DisplayConfig(mode.vrefresh, + mode.hdisplay, + mode.vdisplay, + dpiX, dpiY); + // add it to the front of other configs + mDisplayConfigs.push_front(config); + + // init the active display config + mActiveDisplayConfig = 0; + + drmModeModeInfoPtr modes; + drmModeModeInfoPtr compatMode; + int modeCount = 0; + + modes = drm->detectAllConfigs(mType, &modeCount); + + for (int i = 0; i < modeCount; i++) { + if (modes) { + compatMode = &modes[i]; + if (!compatMode) + continue; + if (compatMode->hdisplay == mode.hdisplay && + compatMode->vdisplay == mode.vdisplay && + compatMode->vrefresh != mode.vrefresh) { + + bool found = false; + for (size_t j = 0; j < mDisplayConfigs.size(); j++) { + DisplayConfig *config = mDisplayConfigs.itemAt(j); + if (config->getRefreshRate() == (int)compatMode->vrefresh) { + found = true; + break; + } + } + + if (found) { + continue; + } + + DisplayConfig *config = new DisplayConfig(compatMode->vrefresh, + compatMode->hdisplay, + compatMode->vdisplay, + dpiX, dpiY); + // add it to the end of configs + mDisplayConfigs.push_back(config); + } + } + } + + return true; +} + +bool PhysicalDevice::initialize() +{ + CTRACE(); + + if (mType != DEVICE_PRIMARY && mType != DEVICE_EXTERNAL) { + ETRACE("invalid device type"); + return false; + } + + // detect display configs + bool ret = detectDisplayConfigs(); + if (ret == false) { + DEINIT_AND_RETURN_FALSE("failed to detect display config"); + } + + if (!mControlFactory) { + DEINIT_AND_RETURN_FALSE("failed to provide a controlFactory "); + } + + // create blank control + mBlankControl = mControlFactory->createBlankControl(); + if (!mBlankControl) { + DEINIT_AND_RETURN_FALSE("failed to create blank control"); + } + + // create vsync event observer + mVsyncObserver = new VsyncEventObserver(*this); + if (!mVsyncObserver || !mVsyncObserver->initialize()) { + DEINIT_AND_RETURN_FALSE("failed to create vsync observer"); + } + + mInitialized = true; + return true; +} + +void PhysicalDevice::deinitialize() +{ + Mutex::Autolock _l(mLock); + if (mLayerList) { + DEINIT_AND_DELETE_OBJ(mLayerList); + } + + DEINIT_AND_DELETE_OBJ(mVsyncObserver); + + // destroy blank control + if (mBlankControl) { + delete mBlankControl; + mBlankControl = 0; + } + + if (mControlFactory){ + delete mControlFactory; + mControlFactory = 0; + } + + // remove configs + removeDisplayConfigs(); + + mInitialized = false; +} + +bool PhysicalDevice::isConnected() const +{ + RETURN_FALSE_IF_NOT_INIT(); + + return mConnected; +} + +const char* PhysicalDevice::getName() const +{ + return mName; +} + +int PhysicalDevice::getType() const +{ + return mType; +} + +void PhysicalDevice::onVsync(int64_t timestamp) +{ + RETURN_VOID_IF_NOT_INIT(); + ATRACE("timestamp = %lld", timestamp); + + if (!mConnected) + return; + + // notify hwc + mHwc.vsync(mType, timestamp); +} + +void PhysicalDevice::dump(Dump& d) +{ + Mutex::Autolock _l(mLock); + d.append("-------------------------------------------------------------\n"); + d.append("Device Name: %s (%s)\n", mName, + mConnected ? "connected" : "disconnected"); + d.append("Display configs (count = %d):\n", mDisplayConfigs.size()); + d.append(" CONFIG | VSYNC_PERIOD | WIDTH | HEIGHT | DPI_X | DPI_Y \n"); + d.append("--------+--------------+-------+--------+-------+-------\n"); + for (size_t i = 0; i < mDisplayConfigs.size(); i++) { + DisplayConfig *config = mDisplayConfigs.itemAt(i); + if (config) { + d.append("%s %2d | %4d | %5d | %4d | %3d | %3d \n", + (i == (size_t)mActiveDisplayConfig) ? "* " : " ", + i, + config->getRefreshRate(), + config->getWidth(), + config->getHeight(), + config->getDpiX(), + config->getDpiY()); + } + } + // dump layer list + if (mLayerList) + mLayerList->dump(d); +} + +bool PhysicalDevice::setPowerMode(int mode) +{ + // TODO: set proper blanking modes for HWC 1.4 modes + switch (mode) { + case HWC_POWER_MODE_OFF: + case HWC_POWER_MODE_DOZE: + return blank(true); + case HWC_POWER_MODE_NORMAL: + case HWC_POWER_MODE_DOZE_SUSPEND: + return blank(false); + default: + return false; + } + return false; +} + +int PhysicalDevice::getActiveConfig() +{ + return mActiveDisplayConfig; +} + +bool PhysicalDevice::setActiveConfig(int index) +{ + // TODO: for now only implement in external + if (index == 0) + return true; + return false; +} + +} // namespace intel +} // namespace android diff --git a/merrifield/common/devices/PrimaryDevice.cpp b/merrifield/common/devices/PrimaryDevice.cpp new file mode 100644 index 0000000..4c39f1e --- /dev/null +++ b/merrifield/common/devices/PrimaryDevice.cpp @@ -0,0 +1,84 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <HwcTrace.h> +#include <Drm.h> +#include <Hwcomposer.h> +#include <DrmConfig.h> +#include <PrimaryDevice.h> + +namespace android { +namespace intel { + +PrimaryDevice::PrimaryDevice(Hwcomposer& hwc, DeviceControlFactory* controlFactory) + : PhysicalDevice(DEVICE_PRIMARY, hwc, controlFactory) +{ + CTRACE(); +} + +PrimaryDevice::~PrimaryDevice() +{ + CTRACE(); +} + +bool PrimaryDevice::initialize() +{ + if (!PhysicalDevice::initialize()) { + DEINIT_AND_RETURN_FALSE("failed to initialize physical device"); + } + + UeventObserver *observer = Hwcomposer::getInstance().getUeventObserver(); + if (observer) { + observer->registerListener( + DrmConfig::getRepeatedFrameString(), + repeatedFrameEventListener, + this); + } else { + ETRACE("Uevent observer is NULL"); + } + + return true; +} + +void PrimaryDevice::deinitialize() +{ + PhysicalDevice::deinitialize(); +} + + +void PrimaryDevice::repeatedFrameEventListener(void *data) +{ + PrimaryDevice *pThis = (PrimaryDevice*)data; + if (pThis) { + pThis->repeatedFrameListener(); + } +} + +void PrimaryDevice::repeatedFrameListener() +{ + Hwcomposer::getInstance().getDisplayAnalyzer()->postIdleEntryEvent(); + Hwcomposer::getInstance().invalidate(); +} + +bool PrimaryDevice::blank(bool blank) +{ + if (!mConnected) + return true; + + return PhysicalDevice::blank(blank); +} + +} // namespace intel +} // namespace android diff --git a/merrifield/common/devices/VirtualDevice.cpp b/merrifield/common/devices/VirtualDevice.cpp new file mode 100755 index 0000000..a4396d7 --- /dev/null +++ b/merrifield/common/devices/VirtualDevice.cpp @@ -0,0 +1,2288 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <HwcTrace.h> +#include <Hwcomposer.h> +#include <DisplayPlaneManager.h> +#include <DisplayQuery.h> +#include <VirtualDevice.h> +#include <SoftVsyncObserver.h> + +#include <binder/IServiceManager.h> +#include <binder/ProcessState.h> + +#include <hal_public.h> +#include <libsync/sw_sync.h> +#include <sync/sync.h> + +#include <va/va_android.h> +#include <va/va_vpp.h> +#include <va/va_tpi.h> + +#include <cutils/properties.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#define NUM_CSC_BUFFERS 6 +#define NUM_SCALING_BUFFERS 3 + +#define QCIF_WIDTH 176 +#define QCIF_HEIGHT 144 + +namespace android { +namespace intel { + +static inline uint32_t align_width(uint32_t val) +{ + return align_to(val, 64); +} + +static inline uint32_t align_height(uint32_t val) +{ + return align_to(val, 16); +} + +static void my_close_fence(const char* func, const char* fenceName, int& fenceFd) +{ + if (fenceFd != -1) { + ALOGV("%s: closing fence %s (fd=%d)", func, fenceName, fenceFd); + int err = close(fenceFd); + if (err < 0) { + ALOGE("%s: fence %s close error %d: %s", func, fenceName, err, strerror(errno)); + } + fenceFd = -1; + } +} + +static void my_sync_wait_and_close(const char* func, const char* fenceName, int& fenceFd) +{ + if (fenceFd != -1) { + ALOGV("%s: waiting on fence %s (fd=%d)", func, fenceName, fenceFd); + int err = sync_wait(fenceFd, 300); + if (err < 0) { + ALOGE("%s: fence %s sync_wait error %d: %s", func, fenceName, err, strerror(errno)); + } + my_close_fence(func, fenceName, fenceFd); + } +} + +static void my_timeline_inc(const char* func, const char* timelineName, int& syncTimelineFd) +{ + if (syncTimelineFd != -1) { + ALOGV("%s: incrementing timeline %s (fd=%d)", func, timelineName, syncTimelineFd); + int err = sw_sync_timeline_inc(syncTimelineFd, 1); + if (err < 0) + ALOGE("%s sync timeline %s increment error %d: %s", func, timelineName, errno, strerror(errno)); + syncTimelineFd = -1; + } +} + +#define CLOSE_FENCE(fenceName) my_close_fence(__func__, #fenceName, fenceName) +#define SYNC_WAIT_AND_CLOSE(fenceName) my_sync_wait_and_close(__func__, #fenceName, fenceName) +#define TIMELINE_INC(timelineName) my_timeline_inc(__func__, #timelineName, timelineName) + +class MappedSurface { +public: + MappedSurface(VADisplay dpy, VASurfaceID surf) + : va_dpy(dpy), + ptr(NULL) + { + VAStatus va_status; + va_status = vaDeriveImage(va_dpy, surf, &image); + if (va_status != VA_STATUS_SUCCESS) { + ETRACE("vaDeriveImage returns %08x", va_status); + return; + } + va_status = vaMapBuffer(va_dpy, image.buf, (void**)&ptr); + if (va_status != VA_STATUS_SUCCESS) { + ETRACE("vaMapBuffer returns %08x", va_status); + vaDestroyImage(va_dpy, image.image_id); + return; + } + } + ~MappedSurface() { + if (ptr == NULL) + return; + + VAStatus va_status; + + va_status = vaUnmapBuffer(va_dpy, image.buf); + if (va_status != VA_STATUS_SUCCESS) ETRACE("vaUnmapBuffer returns %08x", va_status); + + va_status = vaDestroyImage(va_dpy, image.image_id); + if (va_status != VA_STATUS_SUCCESS) ETRACE("vaDestroyImage returns %08x", va_status); + } + bool valid() { return ptr != NULL; } + uint8_t* getPtr() { return ptr; } +private: + VADisplay va_dpy; + VAImage image; + uint8_t* ptr; +}; + +class VirtualDevice::VAMappedHandle { +public: + VAMappedHandle(VADisplay dpy, buffer_handle_t handle, uint32_t stride, uint32_t height, unsigned int pixel_format) + : va_dpy(dpy), + surface(0) + { + VTRACE("Map gralloc %p size=%ux%u", handle, stride, height); + + unsigned int format; + unsigned long buffer = reinterpret_cast<unsigned long>(handle); + VASurfaceAttribExternalBuffers buf; + buf.pixel_format = pixel_format; + buf.width = stride; + buf.height = height; + buf.buffers = &buffer; + buf.num_buffers = 1; + buf.flags = 0; + buf.private_data = NULL; + + if (pixel_format == VA_FOURCC_RGBA || pixel_format == VA_FOURCC_BGRA) { + format = VA_RT_FORMAT_RGB32; + buf.data_size = stride * height * 4; + buf.num_planes = 3; + buf.pitches[0] = stride; + buf.pitches[1] = stride; + buf.pitches[2] = stride; + buf.pitches[3] = 0; + buf.offsets[0] = 0; + buf.offsets[1] = 0; + buf.offsets[2] = 0; + buf.offsets[3] = 0; + } + else { + format = VA_RT_FORMAT_YUV420; + buf.data_size = stride * height * 3/2; + buf.num_planes = 2; + buf.pitches[0] = stride; + buf.pitches[1] = stride; + buf.pitches[2] = 0; + buf.pitches[3] = 0; + buf.offsets[0] = 0; + buf.offsets[1] = stride * height; + } + + VASurfaceAttrib attrib_list[3]; + attrib_list[0].type = (VASurfaceAttribType)VASurfaceAttribMemoryType; + attrib_list[0].flags = VA_SURFACE_ATTRIB_SETTABLE; + attrib_list[0].value.type = VAGenericValueTypeInteger; + attrib_list[0].value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_ANDROID_GRALLOC; + attrib_list[1].type = (VASurfaceAttribType)VASurfaceAttribExternalBufferDescriptor; + attrib_list[1].flags = VA_SURFACE_ATTRIB_SETTABLE; + attrib_list[1].value.type = VAGenericValueTypePointer; + attrib_list[1].value.value.p = (void *)&buf; + attrib_list[2].type = (VASurfaceAttribType)VASurfaceAttribPixelFormat; + attrib_list[2].flags = VA_SURFACE_ATTRIB_SETTABLE; + attrib_list[2].value.type = VAGenericValueTypeInteger; + attrib_list[2].value.value.i = pixel_format; + + VAStatus va_status; + va_status = vaCreateSurfaces(va_dpy, + format, + stride, + height, + &surface, + 1, + attrib_list, + 3); + if (va_status != VA_STATUS_SUCCESS) { + ETRACE("vaCreateSurfaces returns %08x, surface = %x", va_status, surface); + surface = 0; + } + } + VAMappedHandle(VADisplay dpy, buffer_handle_t khandle, uint32_t stride, uint32_t height, bool tiled) + : va_dpy(dpy), + surface(0) + { + int format; + VASurfaceAttributeTPI attribTpi; + memset(&attribTpi, 0, sizeof(attribTpi)); + VTRACE("Map khandle 0x%x size=%ux%u", khandle, stride, height); + attribTpi.type = VAExternalMemoryKernelDRMBufffer; + attribTpi.width = stride; + attribTpi.height = height; + attribTpi.size = stride*height*3/2; + attribTpi.pixel_format = VA_FOURCC_NV12; + attribTpi.tiling = tiled; + attribTpi.luma_stride = stride; + attribTpi.chroma_u_stride = stride; + attribTpi.chroma_v_stride = stride; + attribTpi.luma_offset = 0; + attribTpi.chroma_u_offset = stride*height; + attribTpi.chroma_v_offset = stride*height+1; + format = VA_RT_FORMAT_YUV420; + attribTpi.count = 1; + attribTpi.buffers = (long unsigned int*) &khandle; + + VAStatus va_status; + va_status = vaCreateSurfacesWithAttribute(va_dpy, + stride, + height, + format, + 1, + &surface, + &attribTpi); + if (va_status != VA_STATUS_SUCCESS) { + ETRACE("vaCreateSurfacesWithAttribute returns %08x", va_status); + surface = 0; + } + } + ~VAMappedHandle() + { + if (surface == 0) + return; + VAStatus va_status; + va_status = vaDestroySurfaces(va_dpy, &surface, 1); + if (va_status != VA_STATUS_SUCCESS) ETRACE("vaDestroySurfaces returns %08x", va_status); + } +private: + VADisplay va_dpy; +public: + VASurfaceID surface; +}; + +// refcounted version of VAMappedHandle, to make caching easier +class VirtualDevice::VAMappedHandleObject : public RefBase, public VAMappedHandle { +public: + VAMappedHandleObject(VADisplay dpy, buffer_handle_t handle, uint32_t stride, uint32_t height, unsigned int pixel_format) + : VAMappedHandle(dpy, handle, stride, height, pixel_format) { } + VAMappedHandleObject(VADisplay dpy, buffer_handle_t khandle, uint32_t stride, uint32_t height, bool tiled) + : VAMappedHandle(dpy, khandle, stride, height, tiled) { } +protected: + ~VAMappedHandleObject() {} +}; + +VirtualDevice::CachedBuffer::CachedBuffer(BufferManager *mgr, buffer_handle_t handle) + : manager(mgr), + mapper(NULL), + vaMappedHandle(NULL), + cachedKhandle(0) +{ + DataBuffer *buffer = manager->lockDataBuffer((buffer_handle_t)handle); + mapper = manager->map(*buffer); + manager->unlockDataBuffer(buffer); +} + +VirtualDevice::CachedBuffer::~CachedBuffer() +{ + if (vaMappedHandle != NULL) + delete vaMappedHandle; + manager->unmap(mapper); +} + +VirtualDevice::HeldDecoderBuffer::HeldDecoderBuffer(const sp<VirtualDevice>& vd, const android::sp<CachedBuffer>& cachedBuffer) + : vd(vd), + cachedBuffer(cachedBuffer) +{ + if (!vd->mPayloadManager->setRenderStatus(cachedBuffer->mapper, true)) { + ETRACE("Failed to set render status"); + } +} + +VirtualDevice::HeldDecoderBuffer::~HeldDecoderBuffer() +{ + if (!vd->mPayloadManager->setRenderStatus(cachedBuffer->mapper, false)) { + ETRACE("Failed to set render status"); + } +} + +struct VirtualDevice::Task : public RefBase { + virtual void run(VirtualDevice& vd) = 0; + virtual ~Task() {} +}; + +struct VirtualDevice::RenderTask : public VirtualDevice::Task { + RenderTask() : successful(false) { } + virtual void run(VirtualDevice& vd) = 0; + bool successful; +}; + +struct VirtualDevice::ComposeTask : public VirtualDevice::RenderTask { + ComposeTask() + : videoKhandle(0), + rgbHandle(NULL), + mappedRgbIn(NULL), + outputHandle(NULL), + yuvAcquireFenceFd(-1), + rgbAcquireFenceFd(-1), + outbufAcquireFenceFd(-1), + syncTimelineFd(-1) { } + + virtual ~ComposeTask() { + // If queueCompose() creates this object and sets up fences, + // but aborts before enqueuing the task, or if the task runs + // but errors out, make sure our acquire fences get closed + // and any release fences get signaled. + CLOSE_FENCE(yuvAcquireFenceFd); + CLOSE_FENCE(rgbAcquireFenceFd); + CLOSE_FENCE(outbufAcquireFenceFd); + TIMELINE_INC(syncTimelineFd); + } + + virtual void run(VirtualDevice& vd) { + bool dump = false; + if (vd.mDebugVspDump && ++vd.mDebugCounter > 200) { + dump = true; + vd.mDebugCounter = 0; + } + + SYNC_WAIT_AND_CLOSE(yuvAcquireFenceFd); + + VASurfaceID videoInSurface; + if (videoKhandle == 0) { + videoInSurface = vd.va_blank_yuv_in; + } else { + if (videoCachedBuffer->cachedKhandle != videoKhandle || videoCachedBuffer->vaMappedHandle == NULL) { + if (videoCachedBuffer->vaMappedHandle != NULL) + delete videoCachedBuffer->vaMappedHandle; + videoCachedBuffer->vaMappedHandle = new VAMappedHandle(vd.va_dpy, videoKhandle, videoStride, videoBufHeight, videoTiled); + videoCachedBuffer->cachedKhandle = videoKhandle; + } + videoInSurface = videoCachedBuffer->vaMappedHandle->surface; + } + + if (videoInSurface == 0) { + ETRACE("Couldn't map video"); + return; + } + SYNC_WAIT_AND_CLOSE(rgbAcquireFenceFd); + SYNC_WAIT_AND_CLOSE(outbufAcquireFenceFd); + + VAMappedHandle mappedVideoOut(vd.va_dpy, outputHandle, align_width(outWidth), align_height(outHeight), (unsigned int)VA_FOURCC_NV12); + if (mappedVideoOut.surface == 0) { + ETRACE("Unable to map outbuf"); + return; + } + + if (dump) + dumpSurface(vd.va_dpy, "/data/misc/vsp_in.yuv", videoInSurface, videoStride*videoBufHeight*3/2); + + if (mappedRgbIn != NULL) { + if (dump) + dumpSurface(vd.va_dpy, "/data/misc/vsp_in.rgb", mappedRgbIn->surface, align_width(outWidth)*align_height(outHeight)*4); + vd.vspCompose(videoInSurface, mappedRgbIn->surface, mappedVideoOut.surface, &surface_region, &output_region); + } + else if (rgbHandle != NULL) { + VAMappedHandle localMappedRgbIn(vd.va_dpy, rgbHandle, align_width(outWidth), align_height(outHeight), (unsigned int)VA_FOURCC_BGRA); + vd.vspCompose(videoInSurface, localMappedRgbIn.surface, mappedVideoOut.surface, &surface_region, &output_region); + } + else { + // No RGBA, so compose with 100% transparent RGBA frame. + if (dump) + dumpSurface(vd.va_dpy, "/data/misc/vsp_in.rgb", vd.va_blank_rgb_in, align_width(outWidth)*align_height(outHeight)*4); + vd.vspCompose(videoInSurface, vd.va_blank_rgb_in, mappedVideoOut.surface, &surface_region, &output_region); + } + if (dump) + dumpSurface(vd.va_dpy, "/data/misc/vsp_out.yuv", mappedVideoOut.surface, align_width(outWidth)*align_height(outHeight)*3/2); + TIMELINE_INC(syncTimelineFd); + successful = true; + } + void dumpSurface(VADisplay va_dpy, const char* filename, VASurfaceID surf, int size) { + MappedSurface dumpSurface(va_dpy, surf); + if (dumpSurface.valid()) { + int fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + if (fd > 0) { + write(fd, dumpSurface.getPtr(), size); + close(fd); + ALOGI("Output dumped"); + } + else + ALOGE("Error %d opening output file: %s", errno, strerror(errno)); + } + else + ALOGE("Failed to map output for dump"); + } + buffer_handle_t videoKhandle; + uint32_t videoStride; + uint32_t videoBufHeight; + bool videoTiled; + buffer_handle_t rgbHandle; + sp<RefBase> heldRgbHandle; + sp<VAMappedHandleObject> mappedRgbIn; + buffer_handle_t outputHandle; + VARectangle surface_region; + VARectangle output_region; + uint32_t outWidth; + uint32_t outHeight; + sp<CachedBuffer> videoCachedBuffer; + sp<RefBase> heldVideoBuffer; + int yuvAcquireFenceFd; + int rgbAcquireFenceFd; + int outbufAcquireFenceFd; + int syncTimelineFd; +}; + +struct VirtualDevice::EnableVspTask : public VirtualDevice::Task { + virtual void run(VirtualDevice& vd) { + vd.vspEnable(width, height); + } + uint32_t width; + uint32_t height; +}; + +struct VirtualDevice::DisableVspTask : public VirtualDevice::Task { + virtual void run(VirtualDevice& vd) { + vd.vspDisable(); + } +}; + +struct VirtualDevice::BlitTask : public VirtualDevice::RenderTask { + BlitTask() + : srcAcquireFenceFd(-1), + destAcquireFenceFd(-1), + syncTimelineFd(-1) { } + + virtual ~BlitTask() + { + // If queueColorConvert() creates this object and sets up fences, + // but aborts before enqueuing the task, or if the task runs + // but errors out, make sure our acquire fences get closed + // and any release fences get signaled. + CLOSE_FENCE(srcAcquireFenceFd); + CLOSE_FENCE(destAcquireFenceFd); + TIMELINE_INC(syncTimelineFd); + } + + virtual void run(VirtualDevice& vd) { + SYNC_WAIT_AND_CLOSE(srcAcquireFenceFd); + SYNC_WAIT_AND_CLOSE(destAcquireFenceFd); + BufferManager* mgr = vd.mHwc.getBufferManager(); + if (!(mgr->blit(srcHandle, destHandle, destRect, false, false))) { + ETRACE("color space conversion from RGB to NV12 failed"); + } + else + successful = true; + TIMELINE_INC(syncTimelineFd); + } + buffer_handle_t srcHandle; + buffer_handle_t destHandle; + int srcAcquireFenceFd; + int destAcquireFenceFd; + int syncTimelineFd; + crop_t destRect; +}; + +struct VirtualDevice::FrameTypeChangedTask : public VirtualDevice::Task { + virtual void run(VirtualDevice& vd) { + typeChangeListener->frameTypeChanged(inputFrameInfo); + ITRACE("Notify frameTypeChanged: %dx%d in %dx%d @ %d fps", + inputFrameInfo.contentWidth, inputFrameInfo.contentHeight, + inputFrameInfo.bufferWidth, inputFrameInfo.bufferHeight, + inputFrameInfo.contentFrameRateN); + } + sp<IFrameTypeChangeListener> typeChangeListener; + FrameInfo inputFrameInfo; +}; + +struct VirtualDevice::BufferInfoChangedTask : public VirtualDevice::Task { + virtual void run(VirtualDevice& vd) { + typeChangeListener->bufferInfoChanged(outputFrameInfo); + ITRACE("Notify bufferInfoChanged: %dx%d in %dx%d @ %d fps", + outputFrameInfo.contentWidth, outputFrameInfo.contentHeight, + outputFrameInfo.bufferWidth, outputFrameInfo.bufferHeight, + outputFrameInfo.contentFrameRateN); + } + sp<IFrameTypeChangeListener> typeChangeListener; + FrameInfo outputFrameInfo; +}; + +struct VirtualDevice::OnFrameReadyTask : public VirtualDevice::Task { + virtual void run(VirtualDevice& vd) { + if (renderTask != NULL && !renderTask->successful) + return; + + { + Mutex::Autolock _l(vd.mHeldBuffersLock); + //Add the heldbuffer to the vector before calling onFrameReady, so that the buffer will be removed + //from the vector properly even if the notifyBufferReturned call acquires mHeldBuffersLock first. + vd.mHeldBuffers.add(handle, heldBuffer); + } + + // FIXME: we could remove this casting once onFrameReady receives + // a buffer_handle_t handle + status_t result = frameListener->onFrameReady((uint32_t)handle, handleType, renderTimestamp, mediaTimestamp); + if (result != OK) { + Mutex::Autolock _l(vd.mHeldBuffersLock); + vd.mHeldBuffers.removeItem(handle); + } + } + sp<RenderTask> renderTask; + sp<RefBase> heldBuffer; + sp<IFrameListener> frameListener; + buffer_handle_t handle; + HWCBufferHandleType handleType; + int64_t renderTimestamp; + int64_t mediaTimestamp; +}; + +struct VirtualDevice::BufferList::HeldBuffer : public RefBase { + HeldBuffer(BufferList& list, buffer_handle_t handle, uint32_t w, uint32_t h) + : mList(list), + mHandle(handle), + mWidth(w), + mHeight(h) { } + virtual ~HeldBuffer() + { + Mutex::Autolock _l(mList.mVd.mTaskLock); + if (mWidth == mList.mWidth && mHeight == mList.mHeight) { + VTRACE("Returning %s buffer %p (%ux%u) to list", mList.mName, mHandle, mWidth, mHeight); + mList.mAvailableBuffers.push_back(mHandle); + } else { + VTRACE("Deleting %s buffer %p (%ux%u)", mList.mName, mHandle, mWidth, mHeight); + BufferManager* mgr = mList.mVd.mHwc.getBufferManager(); + mgr->freeGrallocBuffer((mHandle)); + if (mList.mBuffersToCreate < mList.mLimit) + mList.mBuffersToCreate++; + } + } + + BufferList& mList; + buffer_handle_t mHandle; + uint32_t mWidth; + uint32_t mHeight; +}; + +VirtualDevice::BufferList::BufferList(VirtualDevice& vd, const char* name, + uint32_t limit, uint32_t format, uint32_t usage) + : mVd(vd), + mName(name), + mLimit(limit), + mFormat(format), + mUsage(usage), + mBuffersToCreate(0), + mWidth(0), + mHeight(0) +{ +} + +buffer_handle_t VirtualDevice::BufferList::get(uint32_t width, uint32_t height, sp<RefBase>* heldBuffer) +{ + width = align_width(width); + height = align_height(height); + if (mWidth != width || mHeight != height) { + ITRACE("%s buffers changing from %dx%d to %dx%d", + mName, mWidth, mHeight, width, height); + clear(); + mWidth = width; + mHeight = height; + mBuffersToCreate = mLimit; + } + + buffer_handle_t handle; + if (mAvailableBuffers.empty()) { + if (mBuffersToCreate <= 0) + return NULL; + BufferManager* mgr = mVd.mHwc.getBufferManager(); + handle = reinterpret_cast<buffer_handle_t>( + mgr->allocGrallocBuffer(width, height, mFormat, mUsage)); + if (handle == NULL){ + ETRACE("failed to allocate %s buffer", mName); + return NULL; + } + mBuffersToCreate--; + } + else { + handle = *mAvailableBuffers.begin(); + mAvailableBuffers.erase(mAvailableBuffers.begin()); + } + *heldBuffer = new HeldBuffer(*this, handle, width, height); + return handle; +} + +void VirtualDevice::BufferList::clear() +{ + if (mWidth != 0 || mHeight != 0) + ITRACE("Releasing %s buffers (%ux%u)", mName, mWidth, mHeight); + if (!mAvailableBuffers.empty()) { + // iterate the list and call freeGraphicBuffer + for (List<buffer_handle_t>::iterator i = mAvailableBuffers.begin(); i != mAvailableBuffers.end(); ++i) { + VTRACE("Deleting the gralloc buffer associated with handle (%p)", (*i)); + mVd.mHwc.getBufferManager()->freeGrallocBuffer((*i)); + } + mAvailableBuffers.clear(); + } + mWidth = 0; + mHeight = 0; +} + +VirtualDevice::VirtualDevice(Hwcomposer& hwc) + : mProtectedMode(false), + mCscBuffers(*this, "CSC", + NUM_CSC_BUFFERS, DisplayQuery::queryNV12Format(), + GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_PRIVATE_1), + mRgbUpscaleBuffers(*this, "RGB upscale", + NUM_SCALING_BUFFERS, HAL_PIXEL_FORMAT_BGRA_8888, + GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_RENDER), + mInitialized(false), + mHwc(hwc), + mPayloadManager(NULL), + mVsyncObserver(NULL), + mOrigContentWidth(0), + mOrigContentHeight(0), + mFirstVideoFrame(true), + mLastConnectionStatus(false), + mCachedBufferCapcity(16), + mDecWidth(0), + mDecHeight(0) +{ + CTRACE(); + mNextConfig.frameServerActive = false; +} + +VirtualDevice::~VirtualDevice() +{ + WARN_IF_NOT_DEINIT(); +} + +sp<VirtualDevice::CachedBuffer> VirtualDevice::getMappedBuffer(buffer_handle_t handle) +{ + ssize_t index = mMappedBufferCache.indexOfKey(handle); + sp<CachedBuffer> cachedBuffer; + if (index == NAME_NOT_FOUND) { + if (mMappedBufferCache.size() > mCachedBufferCapcity) + mMappedBufferCache.clear(); + + cachedBuffer = new CachedBuffer(mHwc.getBufferManager(), handle); + mMappedBufferCache.add(handle, cachedBuffer); + } else { + cachedBuffer = mMappedBufferCache[index]; + } + + return cachedBuffer; +} + +bool VirtualDevice::threadLoop() +{ + sp<Task> task; + { + Mutex::Autolock _l(mTaskLock); + while (mTasks.empty()) { + mRequestQueued.wait(mTaskLock); + } + task = *mTasks.begin(); + mTasks.erase(mTasks.begin()); + } + if (task != NULL) { + task->run(*this); + task = NULL; + } + mRequestDequeued.signal(); + + return true; +} + +status_t VirtualDevice::start(sp<IFrameTypeChangeListener> typeChangeListener) +{ + ITRACE(); + Mutex::Autolock _l(mConfigLock); + mNextConfig.typeChangeListener = typeChangeListener; + mNextConfig.frameListener = NULL; + mNextConfig.policy.scaledWidth = 0; + mNextConfig.policy.scaledHeight = 0; + mNextConfig.policy.xdpi = 96; + mNextConfig.policy.ydpi = 96; + mNextConfig.policy.refresh = 60; + mNextConfig.extendedModeEnabled = + Hwcomposer::getInstance().getDisplayAnalyzer()->isVideoExtModeEnabled(); + mVideoFramerate = 0; + mFirstVideoFrame = true; + mNextConfig.frameServerActive = true; + mNextConfig.forceNotifyFrameType = true; + mNextConfig.forceNotifyBufferInfo = true; + + return NO_ERROR; +} + +status_t VirtualDevice::stop(bool isConnected) +{ + ITRACE(); + Mutex::Autolock _l(mConfigLock); + mNextConfig.typeChangeListener = NULL; + mNextConfig.frameListener = NULL; + mNextConfig.policy.scaledWidth = 0; + mNextConfig.policy.scaledHeight = 0; + mNextConfig.policy.xdpi = 96; + mNextConfig.policy.ydpi = 96; + mNextConfig.policy.refresh = 60; + mNextConfig.frameServerActive = false; + mNextConfig.extendedModeEnabled = false; + mNextConfig.forceNotifyFrameType = false; + mNextConfig.forceNotifyBufferInfo = false; + { + Mutex::Autolock _l(mTaskLock); + mCscBuffers.clear(); + } + return NO_ERROR; +} + +bool VirtualDevice::isFrameServerActive() const +{ + return mCurrentConfig.frameServerActive; +} + +/* TODO: 64-bit - this handle of size 32-bit is a problem for 64-bit */ +status_t VirtualDevice::notifyBufferReturned(int handle) +{ + CTRACE(); + Mutex::Autolock _l(mHeldBuffersLock); + ssize_t index = mHeldBuffers.indexOfKey((buffer_handle_t)handle); + if (index == NAME_NOT_FOUND) { + ETRACE("Couldn't find returned khandle %p", handle); + } else { + VTRACE("Removing heldBuffer associated with handle (%p)", handle); + mHeldBuffers.removeItemsAt(index, 1); + } + return NO_ERROR; +} + +status_t VirtualDevice::setResolution(const FrameProcessingPolicy& policy, sp<IFrameListener> listener) +{ + ITRACE(); + Mutex::Autolock _l(mConfigLock); + mNextConfig.frameListener = listener; + mNextConfig.policy = policy; + return NO_ERROR; +} + +static bool canUseDirectly(const hwc_display_contents_1_t *display, size_t n) +{ + const hwc_layer_1_t& fbTarget = display->hwLayers[display->numHwLayers-1]; + const hwc_layer_1_t& layer = display->hwLayers[n]; + const IMG_native_handle_t* nativeHandle = reinterpret_cast<const IMG_native_handle_t*>(layer.handle); + return !(layer.flags & HWC_SKIP_LAYER) && layer.transform == 0 && + layer.blending == HWC_BLENDING_PREMULT && + layer.sourceCropf.left == 0 && layer.sourceCropf.top == 0 && + layer.displayFrame.left == 0 && layer.displayFrame.top == 0 && + layer.sourceCropf.right == fbTarget.sourceCropf.right && + layer.sourceCropf.bottom == fbTarget.sourceCropf.bottom && + layer.displayFrame.right == fbTarget.displayFrame.right && + layer.displayFrame.bottom == fbTarget.displayFrame.bottom && + layer.planeAlpha == 255 && layer.handle != NULL && + (nativeHandle->iFormat == HAL_PIXEL_FORMAT_RGBA_8888 || + nativeHandle->iFormat == HAL_PIXEL_FORMAT_BGRA_8888); +} + +bool VirtualDevice::prePrepare(hwc_display_contents_1_t *display) +{ + RETURN_FALSE_IF_NOT_INIT(); + return true; +} + +bool VirtualDevice::prepare(hwc_display_contents_1_t *display) +{ + RETURN_FALSE_IF_NOT_INIT(); + + mRenderTimestamp = systemTime(); + mVspInUse = false; + mExpectAcquireFences = false; + mIsForceCloneMode = false; + + { + Mutex::Autolock _l(mConfigLock); + mCurrentConfig = mNextConfig; + } + + bool shouldBeConnected = (display != NULL); + if (shouldBeConnected != mLastConnectionStatus) { + // calling this will reload the property 'hwc.video.extmode.enable' + Hwcomposer::getInstance().getDisplayAnalyzer()->isVideoExtModeEnabled(); + char propertyVal[PROPERTY_VALUE_MAX]; + if (property_get("widi.compose.rgb_upscale", propertyVal, NULL) > 0) + mVspUpscale = atoi(propertyVal); + if (property_get("widi.compose.all_video", propertyVal, NULL) > 0) + mDebugVspClear = atoi(propertyVal); + if (property_get("widi.compose.dump", propertyVal, NULL) > 0) + mDebugVspDump = atoi(propertyVal); + + Hwcomposer::getInstance().getMultiDisplayObserver()->notifyWidiConnectionStatus(shouldBeConnected); + mLastConnectionStatus = shouldBeConnected; + } + + if (!display) { + // No image. We're done with any mappings and CSC buffers. + mMappedBufferCache.clear(); + Mutex::Autolock _l(mTaskLock); + mCscBuffers.clear(); + return true; + } + + if (!mCurrentConfig.frameServerActive) { + // We're done with CSC buffers, since we blit to outbuf in this mode. + // We want to keep mappings cached, so we don't clear mMappedBufferCache. + Mutex::Autolock _l(mTaskLock); + mCscBuffers.clear(); + } + + // by default send the FRAMEBUFFER_TARGET layer (composited image) + const ssize_t fbTarget = display->numHwLayers-1; + mRgbLayer = fbTarget; + mYuvLayer = -1; + + DisplayAnalyzer *analyzer = mHwc.getDisplayAnalyzer(); + + mProtectedMode = false; + + if (mCurrentConfig.typeChangeListener != NULL && + !analyzer->isOverlayAllowed() && + analyzer->getVideoInstances() <= 1) { + if (mCurrentConfig.typeChangeListener->shutdownVideo() != OK) { + ITRACE("Waiting for prior encoder session to shut down..."); + } + /* Setting following flag to true will enable us to call bufferInfoChanged() in clone mode. */ + mNextConfig.forceNotifyBufferInfo = true; + mYuvLayer = -1; + mRgbLayer = -1; + // Skipping frames. + // Fences aren't set in prepare, and we don't need them here, but they'll + // be set later and we have to close them. Don't log a warning in this case. + mExpectAcquireFences = true; + for (ssize_t i = 0; i < fbTarget; i++) + display->hwLayers[i].compositionType = HWC_OVERLAY; + return true; + } + + for (ssize_t i = 0; i < fbTarget; i++) { + hwc_layer_1_t& layer = display->hwLayers[i]; + if (analyzer->isVideoLayer(layer) && (mCurrentConfig.extendedModeEnabled || mDebugVspClear || analyzer->isProtectedLayer(layer))) { + if (mCurrentConfig.frameServerActive && mCurrentConfig.extendedModeEnabled) { + // If composed in surface flinger, then stream fbtarget. + if ((layer.flags & HWC_SKIP_LAYER) && !analyzer->ignoreVideoSkipFlag()) { + continue; + } + + /* If the resolution of the video layer is less than QCIF, then we are going to play it in clone mode only.*/ + uint32_t vidContentWidth = layer.sourceCropf.right - layer.sourceCropf.left; + uint32_t vidContentHeight = layer.sourceCropf.bottom - layer.sourceCropf.top; + if (vidContentWidth < QCIF_WIDTH || vidContentHeight < QCIF_HEIGHT) { + VTRACE("Ingoring layer %d which is too small for extended mode", i); + continue; + } + } + mYuvLayer = i; + mProtectedMode = analyzer->isProtectedLayer(layer); + break; + } + } + + if (mYuvLayer == -1) { + mFirstVideoFrame = true; + mDecWidth = 0; + mDecHeight = 0; + } + + if (mCurrentConfig.frameServerActive && mCurrentConfig.extendedModeEnabled && mYuvLayer != -1) { + if (handleExtendedMode(display)) { + mYuvLayer = -1; + mRgbLayer = -1; + // Extended mode is successful. + // Fences aren't set in prepare, and we don't need them here, but they'll + // be set later and we have to close them. Don't log a warning in this case. + mExpectAcquireFences = true; + for (ssize_t i = 0; i < fbTarget; i++) + display->hwLayers[i].compositionType = HWC_OVERLAY; + return true; + } + // if error in playback file , switch to clone mode + WTRACE("Error, falling back to clone mode"); + mIsForceCloneMode = true; + mYuvLayer = -1; + } + + if (mYuvLayer == 0 && fbTarget == 1) { + // No RGB layer, so tell queueCompose to use blank RGB in fbtarget. + mRgbLayer = -1; + } + else if (mYuvLayer == 0 && fbTarget == 2) { + if (canUseDirectly(display, 1)) + mRgbLayer = 1; + } + else if (mYuvLayer == -1 && fbTarget == 1) { + if (canUseDirectly(display, 0)) + mRgbLayer = 0; + } + + for (ssize_t i = 0; i < fbTarget; i++) { + hwc_layer_1_t& layer = display->hwLayers[i]; + if (i == mYuvLayer || i == mRgbLayer || mRgbLayer != fbTarget) + layer.compositionType = HWC_OVERLAY; + else + layer.compositionType = HWC_FRAMEBUFFER; + } + if (mYuvLayer != -1 && mRgbLayer == fbTarget) + // This tells SurfaceFlinger to render this layer by writing transparent pixels + // to this layer's target region within the framebuffer. This effectively punches + // a hole through any content that is supposed to show below the video, and the + // video can be seen through this hole when we composite the YUV and RGBA layers + // together. Content above will draw on top of this hole and can cover the video. + // This has no effect when the video is the bottommost layer. + display->hwLayers[mYuvLayer].hints |= HWC_HINT_CLEAR_FB; + + // we're streaming fbtarget, so send onFramePrepare and wait for composition to happen + if (mCurrentConfig.frameListener != NULL) + mCurrentConfig.frameListener->onFramePrepare(mRenderTimestamp, -1); + + return true; +} + +bool VirtualDevice::commit(hwc_display_contents_1_t *display, IDisplayContext *context) +{ + RETURN_FALSE_IF_NOT_INIT(); + + if (display != NULL && (mRgbLayer != -1 || mYuvLayer != -1)) + sendToWidi(display); + + if (mVspEnabled && !mVspInUse) { + mVaMapCache.clear(); + sp<DisableVspTask> disableVsp = new DisableVspTask(); + mMappedBufferCache.clear(); + Mutex::Autolock _l(mTaskLock); + mRgbUpscaleBuffers.clear(); + mTasks.push(disableVsp); + mRequestQueued.signal(); + mVspEnabled = false; + } + + if (display != NULL) { + // All acquire fences should be copied somewhere else or closed by now + // and set to -1 in these structs except in the case of extended mode. + // Make sure the fences are closed and log a warning if not in extended mode. + if (display->outbufAcquireFenceFd != -1) { + if (!mExpectAcquireFences) + WTRACE("outbuf acquire fence (fd=%d) not yet saved or closed", display->outbufAcquireFenceFd); + CLOSE_FENCE(display->outbufAcquireFenceFd); + } + for (size_t i = 0; i < display->numHwLayers; i++) { + hwc_layer_1_t& layer = display->hwLayers[i]; + if (layer.acquireFenceFd != -1) { + if (!mExpectAcquireFences && (i < display->numHwLayers-1 || i == (size_t) mRgbLayer)) + WTRACE("layer %zd acquire fence (fd=%zd) not yet saved or closed", i, layer.acquireFenceFd); + CLOSE_FENCE(layer.acquireFenceFd); + } + } + } + + return true; +} + +bool VirtualDevice::sendToWidi(hwc_display_contents_1_t *display) +{ + VTRACE("RGB=%d, YUV=%d", mRgbLayer, mYuvLayer); + + if (mYuvLayer == -1 && mRgbLayer == -1) + return true; + + if (mYuvLayer != -1) { + mVspInUse = true; + if (queueCompose(display)) + return true; + } + + return queueColorConvert(display); +} + +bool VirtualDevice::queueCompose(hwc_display_contents_1_t *display) +{ + hwc_layer_1_t& yuvLayer = display->hwLayers[mYuvLayer]; + if (yuvLayer.handle == NULL) { + ETRACE("No video handle"); + return false; + } + if (!mCurrentConfig.frameServerActive && display->outbuf == NULL) { + ETRACE("No outbuf"); + return true; // fallback would be pointless + } + + sp<ComposeTask> composeTask = new ComposeTask(); + + sp<RefBase> heldBuffer; + sp<OnFrameReadyTask> frameReadyTask; + Mutex::Autolock _l(mTaskLock); + + float upscale_x = 1.0; + float upscale_y = 1.0; + hwc_layer_1_t& fbTarget = display->hwLayers[display->numHwLayers-1]; + composeTask->outWidth = fbTarget.sourceCropf.right - fbTarget.sourceCropf.left; + composeTask->outHeight = fbTarget.sourceCropf.bottom - fbTarget.sourceCropf.top; + + bool scaleRgb = false; + if (mCurrentConfig.frameServerActive) { + if (mVspUpscale) { + composeTask->outWidth = mCurrentConfig.policy.scaledWidth; + composeTask->outHeight = mCurrentConfig.policy.scaledHeight; + upscale_x = mCurrentConfig.policy.scaledWidth/(fbTarget.sourceCropf.right - fbTarget.sourceCropf.left); + upscale_y = mCurrentConfig.policy.scaledHeight/(fbTarget.sourceCropf.bottom - fbTarget.sourceCropf.top); + scaleRgb = composeTask->outWidth != fbTarget.sourceCropf.right - fbTarget.sourceCropf.left || + composeTask->outHeight != fbTarget.sourceCropf.bottom - fbTarget.sourceCropf.top; + } + + composeTask->outputHandle = mCscBuffers.get(composeTask->outWidth, composeTask->outHeight, &heldBuffer); + if (composeTask->outputHandle == NULL) { + WTRACE("Out of CSC buffers, dropping frame"); + return true; + } + } else { + composeTask->outputHandle = display->outbuf; + } + + vspPrepare(composeTask->outWidth, composeTask->outHeight); + + composeTask->videoCachedBuffer = getMappedBuffer(yuvLayer.handle); + if (composeTask->videoCachedBuffer == NULL) { + ETRACE("Couldn't map video handle %p", yuvLayer.handle); + return false; + } + if (composeTask->videoCachedBuffer->mapper == NULL) { + ETRACE("Src mapper gone"); + return false; + } + composeTask->heldVideoBuffer = new HeldDecoderBuffer(this, composeTask->videoCachedBuffer); + IVideoPayloadManager::MetaData videoMetadata; + if (!mPayloadManager->getMetaData(composeTask->videoCachedBuffer->mapper, &videoMetadata)) { + ETRACE("Failed to map video payload info"); + return false; + } + if (videoMetadata.normalBuffer.width == 0 || videoMetadata.normalBuffer.height == 0) { + ETRACE("Bad video metadata for handle %p", yuvLayer.handle); + return false; + } + if (videoMetadata.normalBuffer.khandle == 0) { + ETRACE("Bad khandle"); + return false; + } + + VARectangle& output_region = composeTask->output_region; + output_region.x = static_cast<uint32_t>(yuvLayer.displayFrame.left*upscale_x) & ~1; + output_region.y = static_cast<uint32_t>(yuvLayer.displayFrame.top*upscale_y) & ~1; + output_region.width = (static_cast<uint32_t>(yuvLayer.displayFrame.right*upscale_y+1) & ~1) - output_region.x; + output_region.height = (static_cast<uint32_t>(yuvLayer.displayFrame.bottom*upscale_y+1) & ~1) - output_region.y; + + uint32_t videoWidth; + uint32_t videoHeight; + if (videoMetadata.transform == 0 || videoMetadata.transform == HAL_TRANSFORM_ROT_180) { + videoWidth = videoMetadata.normalBuffer.width; + videoHeight = videoMetadata.normalBuffer.height; + } else { + videoWidth = videoMetadata.normalBuffer.height; + videoHeight = videoMetadata.normalBuffer.width; + } + + // Layer source crop info is based on an unrotated, unscaled buffer. + // Rotate the rectangle to get the source crop we'd use for a rotated, unscaled buffer. + hwc_frect_t rotatedCrop; + switch (videoMetadata.transform) { + default: + rotatedCrop = yuvLayer.sourceCropf; + break; + case HAL_TRANSFORM_ROT_90: + rotatedCrop.left = yuvLayer.sourceCropf.top; + rotatedCrop.top = videoHeight - yuvLayer.sourceCropf.right; + rotatedCrop.right = yuvLayer.sourceCropf.bottom; + rotatedCrop.bottom = videoHeight - yuvLayer.sourceCropf.left; + break; + case HAL_TRANSFORM_ROT_180: + rotatedCrop.left = videoWidth - yuvLayer.sourceCropf.right; + rotatedCrop.top = videoHeight - yuvLayer.sourceCropf.bottom; + rotatedCrop.right = videoWidth - yuvLayer.sourceCropf.left; + rotatedCrop.bottom = videoHeight - yuvLayer.sourceCropf.top; + break; + case HAL_TRANSFORM_ROT_270: + rotatedCrop.left = videoWidth - yuvLayer.sourceCropf.bottom; + rotatedCrop.top = yuvLayer.sourceCropf.left; + rotatedCrop.right = videoWidth - yuvLayer.sourceCropf.top; + rotatedCrop.bottom = yuvLayer.sourceCropf.right; + break; + } + + float factor_x = output_region.width / (rotatedCrop.right - rotatedCrop.left); + float factor_y = output_region.height / (rotatedCrop.bottom - rotatedCrop.top); + + uint32_t scaleWidth = videoWidth * factor_x; + uint32_t scaleHeight = videoHeight * factor_y; + + scaleWidth &= ~1; + scaleHeight &= ~1; + + IVideoPayloadManager::Buffer info; + if (!getFrameOfSize(scaleWidth, scaleHeight, videoMetadata, info)) { + //Returning true as else we fall into the queueColorConvert + //resulting into scrambled frames for protected content. + ITRACE("scaled frame not yet available."); + return true; + } + + composeTask->videoKhandle = info.khandle; + composeTask->videoStride = info.lumaStride; + composeTask->videoBufHeight = info.bufHeight; + composeTask->videoTiled = info.tiled; + + // rotatedCrop accounts for rotation. Now account for any scaling along each dimension. + hwc_frect_t scaledCrop = rotatedCrop; + if (info.width < videoWidth) { + float factor = static_cast<float>(info.width) / videoWidth; + scaledCrop.left *= factor; + scaledCrop.right *= factor; + } + if (info.height < videoHeight) { + float factor = static_cast<float>(info.height) / videoHeight; + scaledCrop.top *= factor; + scaledCrop.bottom *= factor; + } + + VARectangle& surface_region = composeTask->surface_region; + surface_region.x = static_cast<int>(scaledCrop.left) + info.offsetX; + surface_region.y = static_cast<int>(scaledCrop.top) + info.offsetY; + surface_region.width = static_cast<int>(scaledCrop.right - scaledCrop.left); + surface_region.height = static_cast<int>(scaledCrop.bottom - scaledCrop.top); + + VTRACE("Want to take (%d,%d)-(%d,%d) region from %dx%d video (in %dx%d buffer) and output to (%d,%d)-(%d,%d)", + surface_region.x, surface_region.y, + surface_region.x + surface_region.width, surface_region.y + surface_region.height, + info.width, info.height, + info.bufWidth, info.bufHeight, + output_region.x, output_region.y, + output_region.x + output_region.width, output_region.y + output_region.height); + + if (surface_region.x + surface_region.width > static_cast<int>(info.width + info.offsetX) || + surface_region.y + surface_region.height > static_cast<int>(info.height + info.offsetY)) + { + ETRACE("Source crop exceeds video dimensions: (%d,%d)-(%d,%d) > %ux%u", + surface_region.x, surface_region.y, + surface_region.x + surface_region.width, surface_region.y + surface_region.height, + info.width, info.height); + return false; + } + + if (surface_region.width > output_region.width || surface_region.height > output_region.height) { + // VSP can upscale but can't downscale video, so use blank video + // until we start getting downscaled frames. + surface_region.x = 0; + surface_region.y = 0; + surface_region.width = composeTask->outWidth; + surface_region.height = composeTask->outHeight; + output_region = surface_region; + composeTask->videoKhandle = 0; + composeTask->videoStride = composeTask->outWidth; + composeTask->videoBufHeight = composeTask->outHeight; + composeTask->videoTiled = false; + } + + composeTask->yuvAcquireFenceFd = yuvLayer.acquireFenceFd; + yuvLayer.acquireFenceFd = -1; + + composeTask->outbufAcquireFenceFd = display->outbufAcquireFenceFd; + display->outbufAcquireFenceFd = -1; + + int retireFd = sw_sync_fence_create(mSyncTimelineFd, "widi_compose_retire", mNextSyncPoint); + yuvLayer.releaseFenceFd = retireFd; + + if (mRgbLayer == -1) { + CLOSE_FENCE(fbTarget.acquireFenceFd); + } else { + hwc_layer_1_t& rgbLayer = display->hwLayers[mRgbLayer]; + composeTask->rgbAcquireFenceFd = rgbLayer.acquireFenceFd; + rgbLayer.acquireFenceFd = -1; + rgbLayer.releaseFenceFd = dup(retireFd); + } + + mNextSyncPoint++; + composeTask->syncTimelineFd = mSyncTimelineFd; + + if (mRgbLayer != -1) + { + hwc_layer_1_t& rgbLayer = display->hwLayers[mRgbLayer]; + if (rgbLayer.handle == NULL) { + ETRACE("No RGB handle"); + return false; + } + + if (scaleRgb) { + buffer_handle_t scalingBuffer; + sp<RefBase> heldUpscaleBuffer; + while ((scalingBuffer = mRgbUpscaleBuffers.get(composeTask->outWidth, composeTask->outHeight, &heldUpscaleBuffer)) == NULL && + !mTasks.empty()) { + VTRACE("Waiting for free RGB upscale buffer..."); + mRequestDequeued.wait(mTaskLock); + } + if (scalingBuffer == NULL) { + ETRACE("Couldn't get scaling buffer"); + return false; + } + BufferManager* mgr = mHwc.getBufferManager(); + crop_t destRect; + destRect.x = 0; + destRect.y = 0; + destRect.w = composeTask->outWidth; + destRect.h = composeTask->outHeight; + if (!mgr->blit(rgbLayer.handle, scalingBuffer, destRect, true, true)) + return true; + composeTask->rgbHandle = scalingBuffer; + composeTask->heldRgbHandle = heldUpscaleBuffer; + } + else { + unsigned int pixel_format = VA_FOURCC_BGRA; + const IMG_native_handle_t* nativeHandle = reinterpret_cast<const IMG_native_handle_t*>(rgbLayer.handle); + if (nativeHandle->iFormat == HAL_PIXEL_FORMAT_RGBA_8888) + pixel_format = VA_FOURCC_RGBA; + mRgbUpscaleBuffers.clear(); + ssize_t index = mVaMapCache.indexOfKey(rgbLayer.handle); + if (index == NAME_NOT_FOUND) { + composeTask->mappedRgbIn = new VAMappedHandleObject(va_dpy, rgbLayer.handle, composeTask->outWidth, composeTask->outHeight, pixel_format); + mVaMapCache.add(rgbLayer.handle, composeTask->mappedRgbIn); + } + else + composeTask->mappedRgbIn = mVaMapCache[index]; + if (composeTask->mappedRgbIn->surface == 0) { + ETRACE("Unable to map RGB surface"); + return false; + } + } + } + else + composeTask->mappedRgbIn = NULL; + + mTasks.push_back(composeTask); + mRequestQueued.signal(); + + if (mCurrentConfig.frameServerActive) { + + FrameInfo inputFrameInfo; + memset(&inputFrameInfo, 0, sizeof(inputFrameInfo)); + inputFrameInfo.isProtected = mProtectedMode; + inputFrameInfo.frameType = HWC_FRAMETYPE_FRAME_BUFFER; + if (mVspUpscale) { + float upscale_x = (rotatedCrop.right - rotatedCrop.left) / + (yuvLayer.displayFrame.right - yuvLayer.displayFrame.left); + float upscale_y = (rotatedCrop.bottom - rotatedCrop.top) / + (yuvLayer.displayFrame.bottom - yuvLayer.displayFrame.top); + float upscale = upscale_x > upscale_y ? upscale_x : upscale_y; + if (upscale <= 1.0) + upscale = 1.0; + inputFrameInfo.contentWidth = (fbTarget.sourceCropf.right - fbTarget.sourceCropf.left)*upscale; + inputFrameInfo.contentHeight = (fbTarget.sourceCropf.bottom - fbTarget.sourceCropf.top)*upscale; + } + else { + inputFrameInfo.contentWidth = composeTask->outWidth; + inputFrameInfo.contentHeight = composeTask->outHeight; + } + inputFrameInfo.contentFrameRateN = 0; + inputFrameInfo.contentFrameRateD = 0; + FrameInfo outputFrameInfo = inputFrameInfo; + + BufferManager* mgr = mHwc.getBufferManager(); + DataBuffer* dataBuf = mgr->lockDataBuffer(composeTask->outputHandle); + outputFrameInfo.contentWidth = composeTask->outWidth; + outputFrameInfo.contentHeight = composeTask->outHeight; + outputFrameInfo.bufferWidth = dataBuf->getWidth(); + outputFrameInfo.bufferHeight = dataBuf->getHeight(); + outputFrameInfo.lumaUStride = dataBuf->getWidth(); + outputFrameInfo.chromaUStride = dataBuf->getWidth(); + outputFrameInfo.chromaVStride = dataBuf->getWidth(); + mgr->unlockDataBuffer(dataBuf); + + queueFrameTypeInfo(inputFrameInfo); + if (mCurrentConfig.policy.scaledWidth == 0 || mCurrentConfig.policy.scaledHeight == 0) + return true; // This isn't a failure, WiDi just doesn't want frames right now. + queueBufferInfo(outputFrameInfo); + + if (mCurrentConfig.frameListener != NULL) { + frameReadyTask = new OnFrameReadyTask(); + frameReadyTask->renderTask = composeTask; + frameReadyTask->heldBuffer = heldBuffer; + frameReadyTask->frameListener = mCurrentConfig.frameListener; + frameReadyTask->handle = composeTask->outputHandle; + frameReadyTask->handleType = HWC_HANDLE_TYPE_GRALLOC; + frameReadyTask->renderTimestamp = mRenderTimestamp; + frameReadyTask->mediaTimestamp = -1; + mTasks.push_back(frameReadyTask); + } + } + else { + display->retireFenceFd = dup(retireFd); + } + + return true; +} + +bool VirtualDevice::queueColorConvert(hwc_display_contents_1_t *display) +{ + if (mRgbLayer == -1) { + ETRACE("RGB layer not set"); + return false; + } + hwc_layer_1_t& layer = display->hwLayers[mRgbLayer]; + if (layer.handle == NULL) { + ETRACE("RGB layer has no handle set"); + return false; + } + if (display->outbuf == NULL) { + ETRACE("outbuf is not set"); + return false; + } + + { + const IMG_native_handle_t* nativeSrcHandle = reinterpret_cast<const IMG_native_handle_t*>(layer.handle); + const IMG_native_handle_t* nativeDestHandle = reinterpret_cast<const IMG_native_handle_t*>(display->outbuf); + + if ((nativeSrcHandle->iFormat == HAL_PIXEL_FORMAT_RGBA_8888 && + nativeDestHandle->iFormat == HAL_PIXEL_FORMAT_BGRA_8888) || + (nativeSrcHandle->iFormat == HAL_PIXEL_FORMAT_BGRA_8888 && + nativeDestHandle->iFormat == HAL_PIXEL_FORMAT_RGBA_8888)) + { + SYNC_WAIT_AND_CLOSE(layer.acquireFenceFd); + SYNC_WAIT_AND_CLOSE(display->outbufAcquireFenceFd); + display->retireFenceFd = -1; + + // synchronous in this case + colorSwap(layer.handle, display->outbuf, ((nativeSrcHandle->iWidth+31)&~31)*nativeSrcHandle->iHeight); + // Workaround: Don't keep cached buffers. If the VirtualDisplaySurface gets destroyed, + // these would be unmapped on the next frame, after the buffers are destroyed, + // which is causing heap corruption, probably due to a double-free somewhere. + mMappedBufferCache.clear(); + return true; + } + } + + sp<BlitTask> blitTask = new BlitTask(); + sp<OnFrameReadyTask> frameReadyTask; + blitTask->destRect.x = 0; + blitTask->destRect.y = 0; + blitTask->destRect.w = layer.sourceCropf.right - layer.sourceCropf.left; + blitTask->destRect.h = layer.sourceCropf.bottom - layer.sourceCropf.top; + blitTask->srcHandle = layer.handle; + + sp<RefBase> heldBuffer; + Mutex::Autolock _l(mTaskLock); + + blitTask->srcAcquireFenceFd = layer.acquireFenceFd; + layer.acquireFenceFd = -1; + + blitTask->syncTimelineFd = mSyncTimelineFd; + // Framebuffer after BlitTask::run() calls sw_sync_timeline_inc(). + layer.releaseFenceFd = sw_sync_fence_create(mSyncTimelineFd, "widi_blit_retire", mNextSyncPoint); + mNextSyncPoint++; + + if (mCurrentConfig.frameServerActive) { + blitTask->destHandle = mCscBuffers.get(blitTask->destRect.w, blitTask->destRect.h, &heldBuffer); + blitTask->destAcquireFenceFd = -1; + + // we do not use retire fence in frameServerActive path. + CLOSE_FENCE(display->retireFenceFd); + + // we use our own buffer, so just close this fence without a wait + CLOSE_FENCE(display->outbufAcquireFenceFd); + } + else { + blitTask->destHandle = display->outbuf; + blitTask->destAcquireFenceFd = display->outbufAcquireFenceFd; + // don't let TngDisplayContext::commitEnd() close this + display->outbufAcquireFenceFd = -1; + display->retireFenceFd = dup(layer.releaseFenceFd); + } + + if (blitTask->destHandle == NULL) { + WTRACE("Out of CSC buffers, dropping frame"); + return false; + } + + mTasks.push_back(blitTask); + mRequestQueued.signal(); + + if (mCurrentConfig.frameServerActive) { + FrameInfo inputFrameInfo; + memset(&inputFrameInfo, 0, sizeof(inputFrameInfo)); + inputFrameInfo.isProtected = mProtectedMode; + FrameInfo outputFrameInfo; + + inputFrameInfo.frameType = HWC_FRAMETYPE_FRAME_BUFFER; + inputFrameInfo.contentWidth = blitTask->destRect.w; + inputFrameInfo.contentHeight = blitTask->destRect.h; + inputFrameInfo.contentFrameRateN = 0; + inputFrameInfo.contentFrameRateD = 0; + outputFrameInfo = inputFrameInfo; + + BufferManager* mgr = mHwc.getBufferManager(); + DataBuffer* dataBuf = mgr->lockDataBuffer(blitTask->destHandle); + outputFrameInfo.bufferWidth = dataBuf->getWidth(); + outputFrameInfo.bufferHeight = dataBuf->getHeight(); + outputFrameInfo.lumaUStride = dataBuf->getWidth(); + outputFrameInfo.chromaUStride = dataBuf->getWidth(); + outputFrameInfo.chromaVStride = dataBuf->getWidth(); + mgr->unlockDataBuffer(dataBuf); + + if (!mIsForceCloneMode) + queueFrameTypeInfo(inputFrameInfo); + + if (mCurrentConfig.policy.scaledWidth == 0 || mCurrentConfig.policy.scaledHeight == 0) + return true; // This isn't a failure, WiDi just doesn't want frames right now. + queueBufferInfo(outputFrameInfo); + + if (mCurrentConfig.frameListener != NULL) { + frameReadyTask = new OnFrameReadyTask(); + frameReadyTask->renderTask = blitTask; + frameReadyTask->heldBuffer = heldBuffer; + frameReadyTask->frameListener = mCurrentConfig.frameListener; + frameReadyTask->handle = blitTask->destHandle; + frameReadyTask->handleType = HWC_HANDLE_TYPE_GRALLOC; + frameReadyTask->renderTimestamp = mRenderTimestamp; + frameReadyTask->mediaTimestamp = -1; + mTasks.push_back(frameReadyTask); + } + } + + return true; +} + +bool VirtualDevice::handleExtendedMode(hwc_display_contents_1_t *display) +{ + FrameInfo inputFrameInfo; + memset(&inputFrameInfo, 0, sizeof(inputFrameInfo)); + inputFrameInfo.isProtected = mProtectedMode; + + hwc_layer_1_t& layer = display->hwLayers[mYuvLayer]; + if (layer.handle == NULL) { + ETRACE("video layer has no handle set"); + return false; + } + sp<CachedBuffer> cachedBuffer; + if ((cachedBuffer = getMappedBuffer(layer.handle)) == NULL) { + ETRACE("Failed to map display buffer"); + return false; + } + + inputFrameInfo.frameType = HWC_FRAMETYPE_VIDEO; + // for video mode let 30 fps be the default value. + inputFrameInfo.contentFrameRateN = 30; + inputFrameInfo.contentFrameRateD = 1; + + IVideoPayloadManager::MetaData metadata; + if (!mPayloadManager->getMetaData(cachedBuffer->mapper, &metadata)) { + ETRACE("Failed to get metadata"); + return false; + } + + if (metadata.transform == 0 || metadata.transform == HAL_TRANSFORM_ROT_180) { + inputFrameInfo.contentWidth = metadata.normalBuffer.width; + inputFrameInfo.contentHeight = metadata.normalBuffer.height; + } else { + inputFrameInfo.contentWidth = metadata.normalBuffer.height; + inputFrameInfo.contentHeight = metadata.normalBuffer.width; + // 90 and 270 have some issues that appear to be decoder bugs + ITRACE("Skipping extended mode due to rotation of 90 or 270"); + return false; + } + // Use the crop size if something changed derive it again.. + // Only get video source info if frame rate has not been initialized. + // getVideoSourceInfo() is a fairly expensive operation. This optimization + // will save us a few milliseconds per frame + if (mFirstVideoFrame || (mOrigContentWidth != metadata.normalBuffer.width) || + (mOrigContentHeight != metadata.normalBuffer.height)) { + mVideoFramerate = inputFrameInfo.contentFrameRateN; + VTRACE("VideoWidth = %d, VideoHeight = %d", metadata.normalBuffer.width, metadata.normalBuffer.height); + mOrigContentWidth = metadata.normalBuffer.width; + mOrigContentHeight = metadata.normalBuffer.height; + + // For the first video session by default + int sessionID = Hwcomposer::getInstance().getDisplayAnalyzer()->getFirstVideoInstanceSessionID(); + if (sessionID >= 0) { + ITRACE("Session id = %d", sessionID); + VideoSourceInfo videoInfo; + memset(&videoInfo, 0, sizeof(videoInfo)); + status_t ret = mHwc.getMultiDisplayObserver()->getVideoSourceInfo(sessionID, &videoInfo); + if (ret == NO_ERROR) { + ITRACE("width = %d, height = %d, fps = %d", videoInfo.width, videoInfo.height, + videoInfo.frameRate); + if (videoInfo.frameRate > 0) { + mVideoFramerate = videoInfo.frameRate; + } + } + } + mFirstVideoFrame = false; + } + inputFrameInfo.contentFrameRateN = mVideoFramerate; + inputFrameInfo.contentFrameRateD = 1; + + sp<ComposeTask> composeTask; + sp<RefBase> heldBuffer; + Mutex::Autolock _l(mTaskLock); + + if (mCurrentConfig.policy.scaledWidth == 0 || mCurrentConfig.policy.scaledHeight == 0) { + queueFrameTypeInfo(inputFrameInfo); + return true; // This isn't a failure, WiDi just doesn't want frames right now. + } + + IVideoPayloadManager::Buffer info; + if (!getFrameOfSize(mCurrentConfig.policy.scaledWidth, mCurrentConfig.policy.scaledHeight, metadata, info)) { + ITRACE("Extended mode waiting for scaled frame"); + return false; + } + + queueFrameTypeInfo(inputFrameInfo); + + heldBuffer = new HeldDecoderBuffer(this, cachedBuffer); + int64_t mediaTimestamp = metadata.timestamp; + + VARectangle surface_region; + surface_region.x = info.offsetX; + surface_region.y = info.offsetY; + surface_region.width = info.width; + surface_region.height = info.height; + FrameInfo outputFrameInfo = inputFrameInfo; + outputFrameInfo.bufferFormat = metadata.format; + + outputFrameInfo.contentWidth = info.width; + outputFrameInfo.contentHeight = info.height; + outputFrameInfo.bufferWidth = info.bufWidth; + outputFrameInfo.bufferHeight = info.bufHeight; + outputFrameInfo.lumaUStride = info.lumaStride; + outputFrameInfo.chromaUStride = info.chromaUStride; + outputFrameInfo.chromaVStride = info.chromaVStride; + + if (outputFrameInfo.bufferFormat == 0 || + outputFrameInfo.bufferWidth < outputFrameInfo.contentWidth || + outputFrameInfo.bufferHeight < outputFrameInfo.contentHeight || + outputFrameInfo.contentWidth <= 0 || outputFrameInfo.contentHeight <= 0 || + outputFrameInfo.lumaUStride <= 0 || + outputFrameInfo.chromaUStride <= 0 || outputFrameInfo.chromaVStride <= 0) { + ITRACE("Payload cleared or inconsistent info, not sending frame"); + ITRACE("outputFrameInfo.bufferFormat = %d ", outputFrameInfo.bufferFormat); + ITRACE("outputFrameInfo.bufferWidth = %d ", outputFrameInfo.bufferWidth); + ITRACE("outputFrameInfo.contentWidth = %d ", outputFrameInfo.contentWidth); + ITRACE("outputFrameInfo.bufferHeight = %d ", outputFrameInfo.bufferHeight); + ITRACE("outputFrameInfo.contentHeight = %d ", outputFrameInfo.contentHeight); + ITRACE("outputFrameInfo.lumaUStride = %d ", outputFrameInfo.lumaUStride); + ITRACE("outputFrameInfo.chromaUStride = %d ", outputFrameInfo.chromaUStride); + ITRACE("outputFrameInfo.chromaVStride = %d ", outputFrameInfo.chromaVStride); + return false; + } + + if (mCurrentConfig.policy.scaledWidth == 0 || mCurrentConfig.policy.scaledHeight == 0) + return true; // This isn't a failure, WiDi just doesn't want frames right now. + + if (info.khandle == mExtLastKhandle && mediaTimestamp == mExtLastTimestamp) { + // Same frame again. We don't send a frame, but we return true because + // this isn't an error. + if (metadata.transform != 0) + mVspInUse = true; // Don't shut down VSP just to start it again really quick. + return true; + } + mExtLastKhandle = info.khandle; + mExtLastTimestamp = mediaTimestamp; + + HWCBufferHandleType handleType = HWC_HANDLE_TYPE_KBUF; + + buffer_handle_t handle = info.khandle; + + // Ideally we'd check if there's an offset (info.offsetX > 0 || info.offsetY > 0), + // so we use VSP only when cropping is needed. But using the khandle directly when + // both rotation and scaling are involved can encode the frame with the wrong + // tiling status, so use VSP to normalize if any rotation is involved. + if (metadata.transform != 0) { + // Cropping (or above workaround) needed, so use VSP to do it. + mVspInUse = true; + vspPrepare(info.width, info.height); + + composeTask = new ComposeTask(); + composeTask->heldVideoBuffer = heldBuffer; + heldBuffer = NULL; + composeTask->outWidth = info.width; + composeTask->outHeight = info.height; + composeTask->outputHandle = mCscBuffers.get(composeTask->outWidth, composeTask->outHeight, &heldBuffer); + if (composeTask->outputHandle == NULL) { + ITRACE("Out of CSC buffers, dropping frame"); + return true; + } + + composeTask->surface_region = surface_region; + composeTask->videoCachedBuffer = cachedBuffer; + VARectangle& output_region = composeTask->output_region; + output_region.x = 0; + output_region.y = 0; + output_region.width = info.width; + output_region.height = info.height; + + composeTask->videoKhandle = info.khandle; + composeTask->videoStride = info.lumaStride; + composeTask->videoBufHeight = info.bufHeight; + composeTask->videoTiled = info.tiled; + + BufferManager* mgr = mHwc.getBufferManager(); + DataBuffer* dataBuf = mgr->lockDataBuffer(composeTask->outputHandle); + outputFrameInfo.contentWidth = composeTask->outWidth; + outputFrameInfo.contentHeight = composeTask->outHeight; + outputFrameInfo.bufferWidth = dataBuf->getWidth(); + outputFrameInfo.bufferHeight = dataBuf->getHeight(); + outputFrameInfo.lumaUStride = dataBuf->getWidth(); + outputFrameInfo.chromaUStride = dataBuf->getWidth(); + outputFrameInfo.chromaVStride = dataBuf->getWidth(); + mgr->unlockDataBuffer(dataBuf); + + handle = composeTask->outputHandle; + handleType = HWC_HANDLE_TYPE_GRALLOC; + + mTasks.push_back(composeTask); + mRequestQueued.signal(); + } + + queueBufferInfo(outputFrameInfo); + + if (mCurrentConfig.frameListener != NULL) { + sp<OnFrameReadyTask> frameReadyTask = new OnFrameReadyTask(); + frameReadyTask->renderTask = composeTask; + frameReadyTask->heldBuffer = heldBuffer; + frameReadyTask->frameListener = mCurrentConfig.frameListener; + frameReadyTask->handle = handle; + frameReadyTask->handleType = handleType; + frameReadyTask->renderTimestamp = mRenderTimestamp; + frameReadyTask->mediaTimestamp = mediaTimestamp; + + mTasks.push_back(frameReadyTask); + mRequestQueued.signal(); + } + + return true; +} + +void VirtualDevice::queueFrameTypeInfo(const FrameInfo& inputFrameInfo) +{ + if (mCurrentConfig.forceNotifyFrameType || + memcmp(&inputFrameInfo, &mLastInputFrameInfo, sizeof(inputFrameInfo)) != 0) { + // something changed, notify type change listener + mNextConfig.forceNotifyFrameType = false; + mLastInputFrameInfo = inputFrameInfo; + + sp<FrameTypeChangedTask> notifyTask = new FrameTypeChangedTask; + notifyTask->typeChangeListener = mCurrentConfig.typeChangeListener; + notifyTask->inputFrameInfo = inputFrameInfo; + mTasks.push_back(notifyTask); + } +} + +void VirtualDevice::queueBufferInfo(const FrameInfo& outputFrameInfo) +{ + if (mCurrentConfig.forceNotifyBufferInfo || + memcmp(&outputFrameInfo, &mLastOutputFrameInfo, sizeof(outputFrameInfo)) != 0) { + mNextConfig.forceNotifyBufferInfo = false; + mLastOutputFrameInfo = outputFrameInfo; + + sp<BufferInfoChangedTask> notifyTask = new BufferInfoChangedTask; + notifyTask->typeChangeListener = mCurrentConfig.typeChangeListener; + notifyTask->outputFrameInfo = outputFrameInfo; + + //if (handleType == HWC_HANDLE_TYPE_GRALLOC) + // mMappedBufferCache.clear(); // ! + mTasks.push_back(notifyTask); + } +} + +void VirtualDevice::colorSwap(buffer_handle_t src, buffer_handle_t dest, uint32_t pixelCount) +{ + sp<CachedBuffer> srcCachedBuffer; + sp<CachedBuffer> destCachedBuffer; + + { + srcCachedBuffer = getMappedBuffer(src); + if (srcCachedBuffer == NULL || srcCachedBuffer->mapper == NULL) + return; + destCachedBuffer = getMappedBuffer(dest); + if (destCachedBuffer == NULL || destCachedBuffer->mapper == NULL) + return; + } + + uint8_t* srcPtr = static_cast<uint8_t*>(srcCachedBuffer->mapper->getCpuAddress(0)); + uint8_t* destPtr = static_cast<uint8_t*>(destCachedBuffer->mapper->getCpuAddress(0)); + if (srcPtr == NULL || destPtr == NULL) + return; + while (pixelCount > 0) { + destPtr[0] = srcPtr[2]; + destPtr[1] = srcPtr[1]; + destPtr[2] = srcPtr[0]; + destPtr[3] = srcPtr[3]; + srcPtr += 4; + destPtr += 4; + pixelCount--; + } +} + +void VirtualDevice::vspPrepare(uint32_t width, uint32_t height) +{ + if (mVspEnabled && width == mVspWidth && height == mVspHeight) + return; + + if (mVspEnabled) + { + ITRACE("Going to switch VSP from %ux%u to %ux%u", mVspWidth, mVspHeight, width, height); + mMappedBufferCache.clear(); + mVaMapCache.clear(); + sp<DisableVspTask> disableVsp = new DisableVspTask(); + mTasks.push_back(disableVsp); + } + mVspWidth = width; + mVspHeight = height; + + sp<EnableVspTask> enableTask = new EnableVspTask(); + enableTask->width = width; + enableTask->height = height; + mTasks.push_back(enableTask); + mRequestQueued.signal(); + // to map a buffer from this thread, we need this task to complete on the other thread + while (enableTask->getStrongCount() > 1) { + VTRACE("Waiting for WidiBlit thread to enable VSP..."); + mRequestDequeued.wait(mTaskLock); + } + mVspEnabled = true; +} + +void VirtualDevice::vspEnable(uint32_t width, uint32_t height) +{ + width = align_width(width); + height = align_height(height); + ITRACE("Start VSP at %ux%u", width, height); + VAStatus va_status; + + int display = 0; + int major_ver, minor_ver; + va_dpy = vaGetDisplay(&display); + va_status = vaInitialize(va_dpy, &major_ver, &minor_ver); + if (va_status != VA_STATUS_SUCCESS) ETRACE("vaInitialize returns %08x", va_status); + + VAConfigAttrib va_attr; + va_attr.type = VAConfigAttribRTFormat; + va_status = vaGetConfigAttributes(va_dpy, + VAProfileNone, + VAEntrypointVideoProc, + &va_attr, + 1); + if (va_status != VA_STATUS_SUCCESS) ETRACE("vaGetConfigAttributes returns %08x", va_status); + + va_status = vaCreateConfig( + va_dpy, + VAProfileNone, + VAEntrypointVideoProc, + &(va_attr), + 1, + &va_config + ); + if (va_status != VA_STATUS_SUCCESS) ETRACE("vaCreateConfig returns %08x", va_status); + + VADisplayAttribute attr; + attr.type = VADisplayAttribRenderMode; + attr.value = VA_RENDER_MODE_LOCAL_OVERLAY; + va_status = vaSetDisplayAttributes(va_dpy, &attr, 1); + if (va_status != VA_STATUS_SUCCESS) ETRACE("vaSetDisplayAttributes returns %08x", va_status); + + + va_status = vaCreateSurfaces( + va_dpy, + VA_RT_FORMAT_YUV420, + width, + height, + &va_blank_yuv_in, + 1, + NULL, + 0); + if (va_status != VA_STATUS_SUCCESS) ETRACE("vaCreateSurfaces (video in) returns %08x", va_status); + + unsigned long buffer; + VASurfaceAttribExternalBuffers buf; + int stride = align_width(width); + int bufHeight = align_height(height); + buf.pixel_format = VA_FOURCC_RGBA; + buf.width = width; + buf.height = height; + buf.data_size = stride * bufHeight * 4; + buf.num_planes = 3; + buf.pitches[0] = stride; + buf.pitches[1] = stride; + buf.pitches[2] = stride; + buf.pitches[3] = 0; + buf.offsets[0] = 0; + buf.offsets[1] = stride * bufHeight; + buf.offsets[2] = buf.offsets[1]; + buf.offsets[3] = 0; + buf.buffers = &buffer; + buf.num_buffers = 1; + buf.flags = 0; + buf.private_data = NULL; + + VASurfaceAttrib attrib_list[2]; + attrib_list[0].type = (VASurfaceAttribType)VASurfaceAttribMemoryType; + attrib_list[0].flags = VA_SURFACE_ATTRIB_SETTABLE; + attrib_list[0].value.type = VAGenericValueTypeInteger; + attrib_list[0].value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_VA; + attrib_list[1].type = (VASurfaceAttribType)VASurfaceAttribExternalBufferDescriptor; + attrib_list[1].flags = VA_SURFACE_ATTRIB_SETTABLE; + attrib_list[1].value.type = VAGenericValueTypePointer; + attrib_list[1].value.value.p = (void *)&buf; + + va_status = vaCreateSurfaces( + va_dpy, + VA_RT_FORMAT_RGB32, + stride, + bufHeight, + &va_blank_rgb_in, + 1, + attrib_list, + 2); + if (va_status != VA_STATUS_SUCCESS) ETRACE("vaCreateSurfaces (blank rgba in) returns %08x", va_status); + + va_status = vaCreateContext( + va_dpy, + va_config, + stride, + bufHeight, + 0, + &va_blank_yuv_in /* not used by VSP, but libva checks for it */, + 1, + &va_context); + if (va_status != VA_STATUS_SUCCESS) ETRACE("vaCreateContext returns %08x", va_status); + + VASurfaceID tmp_yuv; + va_status = vaCreateSurfaces( + va_dpy, + VA_RT_FORMAT_YUV420, + stride, + bufHeight, + &tmp_yuv, + 1, + NULL, + 0); + if (va_status != VA_STATUS_SUCCESS) ETRACE("vaCreateSurfaces (temp yuv) returns %08x", va_status); + { + MappedSurface mappedVideoIn(va_dpy, tmp_yuv); + if (mappedVideoIn.valid()) { + // Value doesn't matter, as RGBA will be opaque, + // but I don't want random data in here. + memset(mappedVideoIn.getPtr(), 0x0, width*height*3/2); + } + else + ETRACE("Unable to map tmp black surface"); + } + + { + MappedSurface mappedBlankIn(va_dpy, va_blank_rgb_in); + if (mappedBlankIn.valid()) { + // Fill RGBA with opaque black temporarily, in order to generate an + // encrypted black buffer in va_blank_yuv_in to use in place of the + // real frame data during the short interval where we're waiting for + // downscaling to kick in. + uint32_t* pixels = reinterpret_cast<uint32_t*>(mappedBlankIn.getPtr()); + for (size_t i = 0; i < stride*height; i++) + pixels[i] = 0xff000000; + } + else + ETRACE("Unable to map blank rgba in"); + } + + // Compose opaque black with temp yuv to produce encrypted black yuv. + VARectangle region; + region.x = 0; + region.y = 0; + region.width = width; + region.height = height; + vspCompose(tmp_yuv, va_blank_rgb_in, va_blank_yuv_in, ®ion, ®ion); + + va_status = vaDestroySurfaces(va_dpy, &tmp_yuv, 1); + if (va_status != VA_STATUS_SUCCESS) ETRACE("vaDestroySurfaces (temp yuv) returns %08x", va_status); + + { + // Fill RGBA with transparent black now, to be used when there is no + // UI to compose on top of the video. + MappedSurface mappedBlankIn(va_dpy, va_blank_rgb_in); + if (mappedBlankIn.valid()) + memset(mappedBlankIn.getPtr(), 0, stride*height*4); + else + ETRACE("Unable to map blank rgba in"); + } +} + +void VirtualDevice::vspDisable() +{ + ITRACE("Shut down VSP"); + + if (va_context == 0 && va_blank_yuv_in == 0) { + ITRACE("Already shut down"); + return; + } + + VABufferID pipeline_param_id; + VAStatus va_status; + va_status = vaCreateBuffer(va_dpy, + va_context, + VAProcPipelineParameterBufferType, + sizeof(VAProcPipelineParameterBuffer), + 1, + NULL, + &pipeline_param_id); + if (va_status != VA_STATUS_SUCCESS) ETRACE("vaCreateBuffer returns %08x", va_status); + + VABlendState blend_state; + VAProcPipelineParameterBuffer *pipeline_param; + va_status = vaMapBuffer(va_dpy, + pipeline_param_id, + (void **)&pipeline_param); + if (va_status != VA_STATUS_SUCCESS) ETRACE("vaMapBuffer returns %08x", va_status); + + memset(pipeline_param, 0, sizeof(VAProcPipelineParameterBuffer)); + pipeline_param->pipeline_flags = VA_PIPELINE_FLAG_END; + pipeline_param->num_filters = 0; + pipeline_param->blend_state = &blend_state; + + va_status = vaUnmapBuffer(va_dpy, pipeline_param_id); + if (va_status != VA_STATUS_SUCCESS) ETRACE("vaUnmapBuffer returns %08x", va_status); + + va_status = vaBeginPicture(va_dpy, va_context, va_blank_yuv_in /* just need some valid surface */); + if (va_status != VA_STATUS_SUCCESS) ETRACE("vaBeginPicture returns %08x", va_status); + + va_status = vaRenderPicture(va_dpy, va_context, &pipeline_param_id, 1); + if (va_status != VA_STATUS_SUCCESS) ETRACE("vaRenderPicture returns %08x", va_status); + + va_status = vaEndPicture(va_dpy, va_context); + if (va_status != VA_STATUS_SUCCESS) ETRACE("vaEndPicture returns %08x", va_status); + + va_status = vaDestroyContext(va_dpy, va_context); + if (va_status != VA_STATUS_SUCCESS) ETRACE("vaDestroyContext returns %08x", va_status); + va_context = 0; + + va_status = vaDestroySurfaces(va_dpy, &va_blank_yuv_in, 1); + if (va_status != VA_STATUS_SUCCESS) ETRACE("vaDestroySurfaces (video in) returns %08x", va_status); + va_blank_yuv_in = 0; + + va_status = vaDestroySurfaces(va_dpy, &va_blank_rgb_in, 1); + if (va_status != VA_STATUS_SUCCESS) ETRACE("vaDestroySurfaces (blank rgba in) returns %08x", va_status); + + if (va_config) { + vaDestroyConfig(va_dpy, va_config); + va_config = 0; + } + if (va_dpy) { + vaTerminate(va_dpy); + va_dpy = NULL; + } +} + +void VirtualDevice::vspCompose(VASurfaceID videoIn, VASurfaceID rgbIn, VASurfaceID videoOut, + const VARectangle* surface_region, const VARectangle* output_region) +{ + VAStatus va_status; + + VABufferID pipeline_param_id; + va_status = vaCreateBuffer(va_dpy, + va_context, + VAProcPipelineParameterBufferType, + sizeof(VAProcPipelineParameterBuffer), + 1, + NULL, + &pipeline_param_id); + if (va_status != VA_STATUS_SUCCESS) ETRACE("vaCreateBuffer returns %08x", va_status); + + VABlendState blend_state; + + VAProcPipelineParameterBuffer *pipeline_param; + va_status = vaMapBuffer(va_dpy, + pipeline_param_id, + (void **)&pipeline_param); + if (va_status != VA_STATUS_SUCCESS) ETRACE("vaMapBuffer returns %08x", va_status); + + memset(pipeline_param, 0, sizeof(VAProcPipelineParameterBuffer)); + pipeline_param->surface = videoIn; + pipeline_param->surface_region = surface_region; + pipeline_param->output_region = output_region; + + pipeline_param->pipeline_flags = 0; + pipeline_param->num_filters = 0; + pipeline_param->blend_state = &blend_state; + pipeline_param->num_additional_outputs = 1; + pipeline_param->additional_outputs = &rgbIn; + + va_status = vaUnmapBuffer(va_dpy, pipeline_param_id); + if (va_status != VA_STATUS_SUCCESS) ETRACE("vaUnmapBuffer returns %08x", va_status); + + va_status = vaBeginPicture(va_dpy, va_context, videoOut); + if (va_status != VA_STATUS_SUCCESS) ETRACE("vaBeginPicture returns %08x", va_status); + + va_status = vaRenderPicture(va_dpy, va_context, &pipeline_param_id, 1); + if (va_status != VA_STATUS_SUCCESS) ETRACE("vaRenderPicture returns %08x", va_status); + + va_status = vaEndPicture(va_dpy, va_context); + if (va_status != VA_STATUS_SUCCESS) ETRACE("vaEndPicture returns %08x", va_status); + + va_status = vaSyncSurface(va_dpy, videoOut); + if (va_status != VA_STATUS_SUCCESS) ETRACE("vaSyncSurface returns %08x", va_status); +} + +static uint32_t min(uint32_t a, uint32_t b) +{ + return (a < b) ? a : b; +} + +bool VirtualDevice::getFrameOfSize(uint32_t width, uint32_t height, const IVideoPayloadManager::MetaData& metadata, IVideoPayloadManager::Buffer& info) +{ + if (metadata.transform == 0 || metadata.transform == HAL_TRANSFORM_ROT_180) + setMaxDecodeResolution(min(width, metadata.normalBuffer.width), min(height, metadata.normalBuffer.height)); + else + setMaxDecodeResolution(min(height, metadata.normalBuffer.width), min(width, metadata.normalBuffer.height)); + + if (metadata.transform == 0) { + if (metadata.normalBuffer.khandle != 0 && metadata.normalBuffer.width <= width && metadata.normalBuffer.height <= height) { + info = metadata.normalBuffer; + return true; + } + + if (metadata.scalingBuffer.khandle != 0 && metadata.scalingBuffer.width <= width && metadata.scalingBuffer.height <= height) { + info = metadata.scalingBuffer; + return true; + } + } else { + if (metadata.rotationBuffer.khandle != 0 && metadata.rotationBuffer.width <= width && metadata.rotationBuffer.height <= height) { + info = metadata.rotationBuffer; + return true; + } + } + + return false; +} + +void VirtualDevice::setMaxDecodeResolution(uint32_t width, uint32_t height) +{ + if (mDecWidth == width && mDecHeight == height) + return; + + int sessionID = mHwc.getDisplayAnalyzer()->getFirstVideoInstanceSessionID(); + if (sessionID < 0) { + ETRACE("Session id is less than 0"); + return; + } + + MultiDisplayObserver* mds = mHwc.getMultiDisplayObserver(); + status_t ret = mds->setDecoderOutputResolution(sessionID, width, height, 0, 0, width, height); + if (ret != NO_ERROR) { + ETRACE("Failed to set scaling to %ux%u: %x", width, height, ret); + return; + } + + mDecWidth = width; + mDecHeight = height; + ITRACE("Set scaling to %ux%u",mDecWidth, mDecHeight); +} + +bool VirtualDevice::vsyncControl(bool enabled) +{ + RETURN_FALSE_IF_NOT_INIT(); + return mVsyncObserver->control(enabled); +} + +bool VirtualDevice::blank(bool blank) +{ + RETURN_FALSE_IF_NOT_INIT(); + return true; +} + +bool VirtualDevice::getDisplaySize(int *width, int *height) +{ + RETURN_FALSE_IF_NOT_INIT(); + if (!width || !height) { + ETRACE("invalid parameters"); + return false; + } + + // TODO: make this platform specifc + *width = 1280; + *height = 720; + return true; +} + +bool VirtualDevice::getDisplayConfigs(uint32_t *configs, + size_t *numConfigs) +{ + RETURN_FALSE_IF_NOT_INIT(); + if (!configs || !numConfigs) { + ETRACE("invalid parameters"); + return false; + } + + *configs = 0; + *numConfigs = 1; + + return true; +} + +bool VirtualDevice::getDisplayAttributes(uint32_t configs, + const uint32_t *attributes, + int32_t *values) +{ + RETURN_FALSE_IF_NOT_INIT(); + + if (!attributes || !values) { + ETRACE("invalid parameters"); + return false; + } + + int i = 0; + while (attributes[i] != HWC_DISPLAY_NO_ATTRIBUTE) { + switch (attributes[i]) { + case HWC_DISPLAY_VSYNC_PERIOD: + values[i] = 1e9 / 60; + break; + case HWC_DISPLAY_WIDTH: + values[i] = 1280; + break; + case HWC_DISPLAY_HEIGHT: + values[i] = 720; + break; + case HWC_DISPLAY_DPI_X: + values[i] = 0; + break; + case HWC_DISPLAY_DPI_Y: + values[i] = 0; + break; + default: + ETRACE("unknown attribute %d", attributes[i]); + break; + } + i++; + } + + return true; +} + +bool VirtualDevice::compositionComplete() +{ + RETURN_FALSE_IF_NOT_INIT(); + return true; +} + +bool VirtualDevice::initialize() +{ + // Add initialization codes here. If init fails, invoke DEINIT_AND_RETURN_FALSE(); + mNextConfig.typeChangeListener = NULL; + mNextConfig.policy.scaledWidth = 0; + mNextConfig.policy.scaledHeight = 0; + mNextConfig.policy.xdpi = 96; + mNextConfig.policy.ydpi = 96; + mNextConfig.policy.refresh = 60; + mNextConfig.extendedModeEnabled = false; + mNextConfig.forceNotifyFrameType = false; + mNextConfig.forceNotifyBufferInfo = false; + mCurrentConfig = mNextConfig; + mRgbLayer = -1; + mYuvLayer = -1; + + memset(&mLastInputFrameInfo, 0, sizeof(mLastInputFrameInfo)); + memset(&mLastOutputFrameInfo, 0, sizeof(mLastOutputFrameInfo)); + + mPayloadManager = mHwc.getPlatFactory()->createVideoPayloadManager(); + + if (!mPayloadManager) { + DEINIT_AND_RETURN_FALSE("Failed to create payload manager"); + } + + mVsyncObserver = new SoftVsyncObserver(*this); + if (!mVsyncObserver || !mVsyncObserver->initialize()) { + DEINIT_AND_RETURN_FALSE("Failed to create Soft Vsync Observer"); + } + + mSyncTimelineFd = sw_sync_timeline_create(); + mNextSyncPoint = 1; + mExpectAcquireFences = false; + + mThread = new WidiBlitThread(this); + mThread->run("WidiBlit", PRIORITY_URGENT_DISPLAY); + + // Publish frame server service with service manager + status_t ret = defaultServiceManager()->addService(String16("hwc.widi"), this); + if (ret == NO_ERROR) { + ProcessState::self()->startThreadPool(); + mInitialized = true; + } else { + ETRACE("Could not register hwc.widi with service manager, error = %d", ret); + deinitialize(); + } + + mVspEnabled = false; + mVspInUse = false; + mVspWidth = 0; + mVspHeight = 0; + va_dpy = NULL; + va_config = 0; + va_context = 0; + va_blank_yuv_in = 0; + va_blank_rgb_in = 0; + mVspUpscale = false; + mDebugVspClear = false; + mDebugVspDump = false; + mDebugCounter = 0; + + ITRACE("Init done."); + + return mInitialized; +} + +bool VirtualDevice::isConnected() const +{ + return true; +} + +const char* VirtualDevice::getName() const +{ + return "Virtual"; +} + +int VirtualDevice::getType() const +{ + return DEVICE_VIRTUAL; +} + +void VirtualDevice::onVsync(int64_t timestamp) +{ + mHwc.vsync(DEVICE_VIRTUAL, timestamp); +} + +void VirtualDevice::dump(Dump& d) +{ +} + +void VirtualDevice::deinitialize() +{ + VAStatus va_status; + + if (mPayloadManager) { + delete mPayloadManager; + mPayloadManager = NULL; + } + DEINIT_AND_DELETE_OBJ(mVsyncObserver); + mInitialized = false; +} + +bool VirtualDevice::setPowerMode(int /*mode*/) +{ + return true; +} + +int VirtualDevice::getActiveConfig() +{ + return 0; +} + +bool VirtualDevice::setActiveConfig(int /*index*/) +{ + return false; +} + +} // namespace intel +} // namespace android diff --git a/merrifield/common/observers/MultiDisplayObserver.cpp b/merrifield/common/observers/MultiDisplayObserver.cpp new file mode 100755 index 0000000..9f767b9 --- /dev/null +++ b/merrifield/common/observers/MultiDisplayObserver.cpp @@ -0,0 +1,439 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifdef TARGET_HAS_MULTIPLE_DISPLAY +#include <HwcTrace.h> +#include <binder/IServiceManager.h> +#include <Hwcomposer.h> +#include <DisplayAnalyzer.h> +#include <ExternalDevice.h> +#endif + +#include <MultiDisplayObserver.h> + +namespace android { +namespace intel { + +#ifdef TARGET_HAS_MULTIPLE_DISPLAY + +////// MultiDisplayCallback + +MultiDisplayCallback::MultiDisplayCallback(MultiDisplayObserver *dispObserver) + : mDispObserver(dispObserver), + mVideoState(MDS_VIDEO_STATE_UNKNOWN) +{ +} + +MultiDisplayCallback::~MultiDisplayCallback() +{ + CTRACE(); + mDispObserver = NULL; +} + +status_t MultiDisplayCallback::blankSecondaryDisplay(bool blank) +{ + ITRACE("blank: %d", blank); + mDispObserver->blankSecondaryDisplay(blank); + return NO_ERROR; +} + +status_t MultiDisplayCallback::updateVideoState(int sessionId, MDS_VIDEO_STATE state) +{ + mVideoState = state; + ITRACE("state: %d", state); + mDispObserver->updateVideoState(sessionId, state); + return NO_ERROR; +} + +status_t MultiDisplayCallback::setHdmiTiming(const MDSHdmiTiming& timing) +{ + mDispObserver->setHdmiTiming(timing); + return NO_ERROR; +} + +status_t MultiDisplayCallback::updateInputState(bool state) +{ + //ITRACE("input state: %d", state); + mDispObserver->updateInputState(state); + return NO_ERROR; +} + +status_t MultiDisplayCallback::setHdmiScalingType(MDS_SCALING_TYPE type) +{ + ITRACE("scaling type: %d", type); + // Merrifield doesn't implement this API + return INVALID_OPERATION; +} + +status_t MultiDisplayCallback::setHdmiOverscan(int hValue, int vValue) +{ + ITRACE("oversacn compensation, h: %d v: %d", hValue, vValue); + // Merrifield doesn't implement this API + return INVALID_OPERATION; +} + +////// MultiDisplayObserver + +MultiDisplayObserver::MultiDisplayObserver() + : mMDSCbRegistrar(NULL), + mMDSInfoProvider(NULL), + mMDSConnObserver(NULL), + mMDSDecoderConfig(NULL), + mMDSCallback(NULL), + mLock(), + mCondition(), + mThreadLoopCount(0), + mDeviceConnected(false), + mExternalHdmiTiming(false), + mInitialized(false) +{ + CTRACE(); +} + +MultiDisplayObserver::~MultiDisplayObserver() +{ + WARN_IF_NOT_DEINIT(); +} + +bool MultiDisplayObserver::isMDSRunning() +{ + // Check if Multi Display service is running + sp<IServiceManager> sm = defaultServiceManager(); + if (sm == NULL) { + ETRACE("fail to get service manager!"); + return false; + } + + sp<IBinder> service = sm->checkService(String16(INTEL_MDS_SERVICE_NAME)); + if (service == NULL) { + VTRACE("fail to get MultiDisplay service!"); + return false; + } + + return true; +} + +bool MultiDisplayObserver::initMDSClient() +{ + sp<IServiceManager> sm = defaultServiceManager(); + if (sm == NULL) { + ETRACE("Fail to get service manager"); + return false; + } + sp<IMDService> mds = interface_cast<IMDService>( + sm->getService(String16(INTEL_MDS_SERVICE_NAME))); + if (mds == NULL) { + ETRACE("Fail to get MDS service"); + return false; + } + mMDSCbRegistrar = mds->getCallbackRegistrar(); + if (mMDSCbRegistrar.get() == NULL) { + ETRACE("failed to create mds base Client"); + return false; + } + + mMDSCallback = new MultiDisplayCallback(this); + if (mMDSCallback.get() == NULL) { + ETRACE("failed to create MultiDisplayCallback"); + deinitMDSClient(); + return false; + } + mMDSInfoProvider = mds->getInfoProvider(); + if (mMDSInfoProvider.get() == NULL) { + ETRACE("failed to create mds video Client"); + return false; + } + + mMDSConnObserver = mds->getConnectionObserver(); + if (mMDSConnObserver.get() == NULL) { + ETRACE("failed to create mds video Client"); + return false; + } + mMDSDecoderConfig = mds->getDecoderConfig(); + if (mMDSDecoderConfig.get() == NULL) { + ETRACE("failed to create mds decoder Client"); + return false; + } + + status_t ret = mMDSCbRegistrar->registerCallback(mMDSCallback); + if (ret != NO_ERROR) { + ETRACE("failed to register callback"); + deinitMDSClient(); + return false; + } + + Drm *drm = Hwcomposer::getInstance().getDrm(); + mDeviceConnected = drm->isConnected(IDisplayDevice::DEVICE_EXTERNAL); + ITRACE("MDS client is initialized"); + return true; +} + +void MultiDisplayObserver::deinitMDSClient() +{ + if (mMDSCallback.get() && mMDSCbRegistrar.get()) { + mMDSCbRegistrar->unregisterCallback(mMDSCallback); + } + + mDeviceConnected = false; + mMDSCbRegistrar = NULL; + mMDSInfoProvider = NULL; + mMDSCallback = NULL; + mMDSConnObserver = NULL; + mMDSDecoderConfig = NULL; +} + +bool MultiDisplayObserver::initMDSClientAsync() +{ + if (mThread.get()) { + WTRACE("working thread has been already created."); + return true; + } + + mThread = new MDSClientInitThread(this); + if (mThread.get() == NULL) { + ETRACE("failed to create MDS client init thread"); + return false; + } + mThreadLoopCount = 0; + // TODO: check return value + mThread->run("MDSClientInitThread", PRIORITY_URGENT_DISPLAY); + return true; +} + +bool MultiDisplayObserver::initialize() +{ + bool ret = true; + Mutex::Autolock _l(mLock); + + if (mInitialized) { + WTRACE("display observer has been initialized"); + return true; + } + + // initialize MDS client once. This should succeed if MDS service starts + // before surfaceflinger service is started. + // if surface flinger runs first, MDS client will be initialized asynchronously in + // a working thread + if (isMDSRunning()) { + if (!initMDSClient()) { + ETRACE("failed to initialize MDS client"); + // FIXME: NOT a common case for system server crash. + // Start a working thread to initialize MDS client if exception happens + ret = initMDSClientAsync(); + } + } else { + ret = initMDSClientAsync(); + } + + mInitialized = true; + return ret; +} + +void MultiDisplayObserver::deinitialize() +{ + sp<MDSClientInitThread> detachedThread; + do { + Mutex::Autolock _l(mLock); + + if (mThread.get()) { + mCondition.signal(); + detachedThread = mThread; + mThread = NULL; + } + mThreadLoopCount = 0; + deinitMDSClient(); + mInitialized = false; + } while (0); + + if (detachedThread.get()) { + detachedThread->requestExitAndWait(); + detachedThread = NULL; + } +} + +bool MultiDisplayObserver::threadLoop() +{ + Mutex::Autolock _l(mLock); + + // try to create MDS client in the working thread + // multiple delayed attempts are made until MDS service starts. + + // Return false if MDS service is running or loop limit is reached + // such that thread becomes inactive. + if (isMDSRunning()) { + if (!initMDSClient()) { + ETRACE("failed to initialize MDS client"); + } + return false; + } + + if (mThreadLoopCount++ > THREAD_LOOP_BOUND) { + ETRACE("failed to initialize MDS client, loop limit reached"); + return false; + } + + status_t err = mCondition.waitRelative(mLock, milliseconds(THREAD_LOOP_DELAY)); + if (err != -ETIMEDOUT) { + ITRACE("thread is interrupted"); + return false; + } + + return true; // keep trying +} + + +status_t MultiDisplayObserver::blankSecondaryDisplay(bool blank) +{ + // blank secondary display + Hwcomposer::getInstance().getDisplayAnalyzer()->postBlankEvent(blank); + return 0; +} + +status_t MultiDisplayObserver::updateVideoState(int sessionId, MDS_VIDEO_STATE state) +{ + Hwcomposer::getInstance().getDisplayAnalyzer()->postVideoEvent( + sessionId, (int)state); + return 0; +} + +status_t MultiDisplayObserver::setHdmiTiming(const MDSHdmiTiming& timing) +{ + drmModeModeInfo mode; + mode.hdisplay = timing.width; + mode.vdisplay = timing.height; + mode.vrefresh = timing.refresh; + mode.flags = timing.flags; + ITRACE("timing to set: %dx%d@%dHz", timing.width, timing.height, timing.refresh); + ExternalDevice *dev = + (ExternalDevice *)Hwcomposer::getInstance().getDisplayDevice(HWC_DISPLAY_EXTERNAL); + if (dev) { + dev->setDrmMode(mode); + } + + mExternalHdmiTiming = true; + return 0; +} + +status_t MultiDisplayObserver::updateInputState(bool active) +{ + Hwcomposer::getInstance().getDisplayAnalyzer()->postInputEvent(active); + return 0; +} + + +/// Public interfaces + +status_t MultiDisplayObserver::notifyHotPlug( bool connected) +{ + { + // lock scope + Mutex::Autolock _l(mLock); + if (mMDSConnObserver.get() == NULL) { + return NO_INIT; + } + + if (connected == mDeviceConnected) { + WTRACE("hotplug event ignored"); + return NO_ERROR; + } + + // clear it after externel device is disconnected + if (!connected) mExternalHdmiTiming = false; + + mDeviceConnected = connected; + } + return mMDSConnObserver->updateHdmiConnectionStatus(connected); +} + +status_t MultiDisplayObserver::getVideoSourceInfo(int sessionID, VideoSourceInfo* info) +{ + Mutex::Autolock _l(mLock); + if (mMDSInfoProvider.get() == NULL) { + return NO_INIT; + } + + if (info == NULL) { + ETRACE("invalid parameter"); + return UNKNOWN_ERROR; + } + + MDSVideoSourceInfo videoInfo; + memset(&videoInfo, 0, sizeof(MDSVideoSourceInfo)); + status_t ret = mMDSInfoProvider->getVideoSourceInfo(sessionID, &videoInfo); + if (ret == NO_ERROR) { + info->width = videoInfo.displayW; + info->height = videoInfo.displayH; + info->frameRate = videoInfo.frameRate; + info->isProtected = videoInfo.isProtected; + VTRACE("Video Session[%d] source info: %dx%d@%d", sessionID, + info->width, info->height, info->frameRate); + } + return ret; +} + +int MultiDisplayObserver::getVideoSessionNumber() +{ + Mutex::Autolock _l(mLock); + if (mMDSInfoProvider.get() == NULL) { + return 0; + } + + return mMDSInfoProvider->getVideoSessionNumber(); +} + +bool MultiDisplayObserver::isExternalDeviceTimingFixed() const +{ + Mutex::Autolock _l(mLock); + return mExternalHdmiTiming; +} + +status_t MultiDisplayObserver::notifyWidiConnectionStatus( bool connected) +{ + Mutex::Autolock _l(mLock); + if (mMDSConnObserver.get() == NULL) { + return NO_INIT; + } + return mMDSConnObserver->updateWidiConnectionStatus(connected); +} + +status_t MultiDisplayObserver::setDecoderOutputResolution( + int sessionID, + int32_t width, int32_t height, + int32_t offX, int32_t offY, + int32_t bufWidth, int32_t bufHeight) +{ + Mutex::Autolock _l(mLock); + if (mMDSDecoderConfig.get() == NULL) { + return NO_INIT; + } + if (width <= 0 || height <= 0 || + offX < 0 || offY < 0 || + bufWidth <= 0 || bufHeight <= 0) { + ETRACE(" Invalid parameter: %dx%d, %dx%d, %dx%d", width, height, offX, offY, bufWidth, bufHeight); + return UNKNOWN_ERROR; + } + + status_t ret = mMDSDecoderConfig->setDecoderOutputResolution(sessionID, width, height, offX, offY, bufWidth, bufHeight); + if (ret == NO_ERROR) { + ITRACE("Video Session[%d] output resolution %dx%d ", sessionID, width, height); + } + return ret; +} + + +#endif //TARGET_HAS_MULTIPLE_DISPLAY + +} // namespace intel +} // namespace android diff --git a/merrifield/common/observers/MultiDisplayObserver.h b/merrifield/common/observers/MultiDisplayObserver.h new file mode 100755 index 0000000..44a113d --- /dev/null +++ b/merrifield/common/observers/MultiDisplayObserver.h @@ -0,0 +1,141 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef __MULTIDISPLAY_OBSERVER_H +#define __MULTIDISPLAY_OBSERVER_H + +#ifdef TARGET_HAS_MULTIPLE_DISPLAY +#include <display/MultiDisplayService.h> +#include <SimpleThread.h> +#else +#include <utils/Errors.h> +#endif +#include <string.h> + +namespace android { +namespace intel { + +struct VideoSourceInfo { + VideoSourceInfo() { + memset(this, 0, sizeof(VideoSourceInfo)); + } + int width; + int height; + int frameRate; + bool isProtected; +}; + + +#ifdef TARGET_HAS_MULTIPLE_DISPLAY + +class MultiDisplayObserver; + +class MultiDisplayCallback : public BnMultiDisplayCallback { +public: + MultiDisplayCallback(MultiDisplayObserver *observer); + virtual ~MultiDisplayCallback(); + + status_t blankSecondaryDisplay(bool blank); + status_t updateVideoState(int sessionId, MDS_VIDEO_STATE state); + status_t setHdmiTiming(const MDSHdmiTiming& timing); + status_t setHdmiScalingType(MDS_SCALING_TYPE type); + status_t setHdmiOverscan(int hValue, int vValue); + status_t updateInputState(bool state); + +private: + MultiDisplayObserver *mDispObserver; + MDS_VIDEO_STATE mVideoState; +}; + +class MultiDisplayObserver { +public: + MultiDisplayObserver(); + virtual ~MultiDisplayObserver(); + +public: + bool initialize(); + void deinitialize(); + status_t notifyHotPlug(bool connected); + status_t getVideoSourceInfo(int sessionID, VideoSourceInfo* info); + int getVideoSessionNumber(); + bool isExternalDeviceTimingFixed() const; + status_t notifyWidiConnectionStatus(bool connected); + status_t setDecoderOutputResolution(int sessionID, + int32_t width, int32_t height, + int32_t offX, int32_t offY, + int32_t bufWidth, int32_t bufHeight); + +private: + bool isMDSRunning(); + bool initMDSClient(); + bool initMDSClientAsync(); + void deinitMDSClient(); + status_t blankSecondaryDisplay(bool blank); + status_t updateVideoState(int sessionId, MDS_VIDEO_STATE state); + status_t setHdmiTiming(const MDSHdmiTiming& timing); + status_t updateInputState(bool active); + friend class MultiDisplayCallback; + +private: + enum { + THREAD_LOOP_DELAY = 10, // 10 ms + THREAD_LOOP_BOUND = 2000, // 20s + }; + +private: + sp<IMultiDisplayCallbackRegistrar> mMDSCbRegistrar; + sp<IMultiDisplayInfoProvider> mMDSInfoProvider; + sp<IMultiDisplayConnectionObserver> mMDSConnObserver; + sp<IMultiDisplayDecoderConfig> mMDSDecoderConfig; + sp<MultiDisplayCallback> mMDSCallback; + mutable Mutex mLock; + Condition mCondition; + int mThreadLoopCount; + bool mDeviceConnected; + // indicate external devices's timing is set + bool mExternalHdmiTiming; + bool mInitialized; + +private: + DECLARE_THREAD(MDSClientInitThread, MultiDisplayObserver); +}; + +#else + +// dummy declaration and implementation of MultiDisplayObserver +class MultiDisplayObserver { +public: + MultiDisplayObserver() {} + virtual ~MultiDisplayObserver() {} + + bool initialize() { return true; } + void deinitialize() {} + status_t notifyHotPlug(bool connected) { return NO_ERROR; } + status_t getVideoSourceInfo(int sessionID, VideoSourceInfo* info) { return INVALID_OPERATION; } + int getVideoSessionNumber() { return 0; } + bool isExternalDeviceTimingFixed() const { return false; } + status_t notifyWidiConnectionStatus(bool connected) { return NO_ERROR; } + status_t setDecoderOutputResolution( + int sessionID, + int32_t width, int32_t height, + int32_t, int32_t, int32_t, int32_t) { return NO_ERROR; } +}; + +#endif //TARGET_HAS_MULTIPLE_DISPLAY + +} // namespace intel +} // namespace android + +#endif /* __MULTIMultiDisplayObserver_H_ */ diff --git a/merrifield/common/observers/SoftVsyncObserver.cpp b/merrifield/common/observers/SoftVsyncObserver.cpp new file mode 100644 index 0000000..20a6bdf --- /dev/null +++ b/merrifield/common/observers/SoftVsyncObserver.cpp @@ -0,0 +1,155 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <HwcTrace.h> +#include <SoftVsyncObserver.h> +#include <IDisplayDevice.h> + +extern "C" int clock_nanosleep(clockid_t clock_id, int flags, + const struct timespec *request, + struct timespec *remain); + + +namespace android { +namespace intel { + +SoftVsyncObserver::SoftVsyncObserver(IDisplayDevice& disp) + : mDisplayDevice(disp), + mDevice(IDisplayDevice::DEVICE_COUNT), + mEnabled(false), + mRefreshRate(60), // default 60 frames per second + mRefreshPeriod(0), + mLock(), + mCondition(), + mNextFakeVSync(0), + mExitThread(false), + mInitialized(false) +{ +} + +SoftVsyncObserver::~SoftVsyncObserver() +{ + WARN_IF_NOT_DEINIT(); +} + +bool SoftVsyncObserver::initialize() +{ + if (mInitialized) { + WTRACE("object has been initialized"); + return true; + } + + mExitThread = false; + mEnabled = false; + mRefreshRate = 60; + mDevice = mDisplayDevice.getType(); + mThread = new VsyncEventPollThread(this); + if (!mThread.get()) { + DEINIT_AND_RETURN_FALSE("failed to create vsync event poll thread."); + } + mThread->run("SoftVsyncObserver", PRIORITY_URGENT_DISPLAY); + mInitialized = true; + return true; +} + +void SoftVsyncObserver::deinitialize() +{ + if (mEnabled) { + WTRACE("soft vsync is still enabled"); + control(false); + } + + mExitThread = true; + mCondition.signal(); + + if (mThread.get()) { + mThread->requestExitAndWait(); + mThread = NULL; + } + mInitialized = false; +} + +void SoftVsyncObserver::setRefreshRate(int rate) +{ + if (mEnabled) { + WTRACE("too late to set refresh rate"); + } else if (rate < 1 || rate > 120) { + WTRACE("invalid refresh rate %d", rate); + } else { + mRefreshRate = rate; + } +} + +bool SoftVsyncObserver::control(bool enabled) +{ + if (enabled == mEnabled) { + WTRACE("vsync state %d is not changed", enabled); + return true; + } + + if (enabled) { + mRefreshPeriod = nsecs_t(1e9 / mRefreshRate); + mNextFakeVSync = systemTime(CLOCK_MONOTONIC) + mRefreshPeriod; + } + mEnabled = enabled; + mCondition.signal(); + return true; +} + +bool SoftVsyncObserver::threadLoop() +{ + { // scope for lock + Mutex::Autolock _l(mLock); + while (!mEnabled) { + mCondition.wait(mLock); + if (mExitThread) { + ITRACE("exiting thread loop"); + return false; + } + } + } + + + const nsecs_t period = mRefreshPeriod; + const nsecs_t now = systemTime(CLOCK_MONOTONIC); + nsecs_t next_vsync = mNextFakeVSync; + nsecs_t sleep = next_vsync - now; + if (sleep < 0) { + // we missed, find where the next vsync should be + sleep = (period - ((now - next_vsync) % period)); + next_vsync = now + sleep; + } + mNextFakeVSync = next_vsync + period; + + struct timespec spec; + spec.tv_sec = next_vsync / 1000000000; + spec.tv_nsec = next_vsync % 1000000000; + + int err; + do { + err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, NULL); + } while (err < 0 && errno == EINTR); + + + if (err == 0) { + mDisplayDevice.onVsync(next_vsync); + } + + return true; +} + +} // namespace intel +} // namesapce android + diff --git a/merrifield/common/observers/SoftVsyncObserver.h b/merrifield/common/observers/SoftVsyncObserver.h new file mode 100755 index 0000000..3550372 --- /dev/null +++ b/merrifield/common/observers/SoftVsyncObserver.h @@ -0,0 +1,59 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef SOFT_VSYNC_OBSERVER_H +#define SOFT_VSYNC_OBSERVER_H + +#include <SimpleThread.h> + +namespace android { +namespace intel { + +class IDisplayDevice; + +class SoftVsyncObserver { +public: + SoftVsyncObserver(IDisplayDevice& disp); + virtual ~SoftVsyncObserver(); + +public: + virtual bool initialize(); + virtual void deinitialize(); + virtual void setRefreshRate(int rate); + virtual bool control(bool enabled); + +private: + IDisplayDevice& mDisplayDevice; + int mDevice; + bool mEnabled; + int mRefreshRate; + nsecs_t mRefreshPeriod; + mutable Mutex mLock; + Condition mCondition; + mutable nsecs_t mNextFakeVSync; + bool mExitThread; + bool mInitialized; + +private: + DECLARE_THREAD(VsyncEventPollThread, SoftVsyncObserver); +}; + +} // namespace intel +} // namespace android + + + +#endif /* SOFT_VSYNC_OBSERVER_H */ + diff --git a/merrifield/common/observers/UeventObserver.cpp b/merrifield/common/observers/UeventObserver.cpp new file mode 100644 index 0000000..5f7ff27 --- /dev/null +++ b/merrifield/common/observers/UeventObserver.cpp @@ -0,0 +1,213 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <poll.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/queue.h> +#include <linux/netlink.h> +#include <sys/types.h> +#include <unistd.h> +#include <DrmConfig.h> +#include <HwcTrace.h> +#include <UeventObserver.h> + +namespace android { +namespace intel { + +UeventObserver::UeventObserver() + : mUeventFd(-1), + mExitRDFd(-1), + mExitWDFd(-1), + mListeners() +{ +} + +UeventObserver::~UeventObserver() +{ + deinitialize(); +} + +bool UeventObserver::initialize() +{ + mListeners.clear(); + + if (mUeventFd != -1) { + return true; + } + + mThread = new UeventObserverThread(this); + if (!mThread.get()) { + ETRACE("failed to create uevent observer thread"); + return false; + } + + // init uevent socket + struct sockaddr_nl addr; + // set the socket receive buffer to 64K + // NOTE: this is only called for once + int sz = 64 * 1024; + + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; + addr.nl_pid = pthread_self() | getpid(); + addr.nl_groups = 0xffffffff; + + mUeventFd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); + if (mUeventFd < 0) { + DEINIT_AND_RETURN_FALSE("failed to create uevent socket"); + } + + if (setsockopt(mUeventFd, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz))) { + WTRACE("setsockopt() failed"); + //return false; + } + + if (bind(mUeventFd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + DEINIT_AND_RETURN_FALSE("failed to bind scoket"); + return false; + } + + memset(mUeventMessage, 0, UEVENT_MSG_LEN); + + int exitFds[2]; + if (pipe(exitFds) < 0) { + ETRACE("failed to make pipe"); + deinitialize(); + return false; + } + mExitRDFd = exitFds[0]; + mExitWDFd = exitFds[1]; + + return true; +} + +void UeventObserver::deinitialize() +{ + if (mUeventFd != -1) { + if (mExitWDFd != -1) { + close(mExitWDFd); + mExitWDFd = -1; + } + close(mUeventFd); + mUeventFd = -1; + } + + if (mThread.get()) { + mThread->requestExitAndWait(); + mThread = NULL; + } + + while (!mListeners.isEmpty()) { + UeventListener *listener = mListeners.valueAt(0); + mListeners.removeItemsAt(0); + delete listener; + } +} + +void UeventObserver::start() +{ + if (mThread.get()) { + mThread->run("UeventObserver", PRIORITY_URGENT_DISPLAY); + } +} + + +void UeventObserver::registerListener(const char *event, UeventListenerFunc func, void *data) +{ + if (!event || !func) { + ETRACE("invalid event string or listener to register"); + return; + } + + String8 key(event); + if (mListeners.indexOfKey(key) >= 0) { + ETRACE("listener for uevent %s exists", event); + return; + } + + UeventListener *listener = new UeventListener; + if (!listener) { + ETRACE("failed to create Uevent Listener"); + return; + } + listener->func = func; + listener->data = data; + + mListeners.add(key, listener); +} + +bool UeventObserver::threadLoop() +{ + if (mUeventFd == -1) { + ETRACE("invalid uEvent file descriptor"); + return false; + } + + struct pollfd fds[2]; + int nr; + + fds[0].fd = mUeventFd; + fds[0].events = POLLIN; + fds[0].revents = 0; + fds[1].fd = mExitRDFd; + fds[1].events = POLLIN; + fds[1].revents = 0; + nr = poll(fds, 2, -1); + + if (nr > 0 && fds[0].revents == POLLIN) { + int count = recv(mUeventFd, mUeventMessage, UEVENT_MSG_LEN - 2, 0); + if (count > 0) { + onUevent(); + } + } else if (fds[1].revents) { + close(mExitRDFd); + mExitRDFd = -1; + ITRACE("exiting wait"); + return false; + } + // always looping + return true; +} + +void UeventObserver::onUevent() +{ + char *msg = mUeventMessage; + const char *envelope = DrmConfig::getUeventEnvelope(); + if (strncmp(msg, envelope, strlen(envelope)) != 0) + return; + + msg += strlen(msg) + 1; + + UeventListener *listener; + String8 key; + while (*msg) { + key = String8(msg); + if (mListeners.indexOfKey(key) >= 0) { + DTRACE("received Uevent: %s", msg); + listener = mListeners.valueFor(key); + if (listener) { + listener->func(listener->data); + } else { + ETRACE("no listener for uevent %s", msg); + } + } + msg += strlen(msg) + 1; + } +} + +} // namespace intel +} // namespace android + diff --git a/merrifield/common/observers/VsyncEventObserver.cpp b/merrifield/common/observers/VsyncEventObserver.cpp new file mode 100644 index 0000000..b7a6fa3 --- /dev/null +++ b/merrifield/common/observers/VsyncEventObserver.cpp @@ -0,0 +1,137 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <HwcTrace.h> +#include <VsyncEventObserver.h> +#include <PhysicalDevice.h> + +namespace android { +namespace intel { + +VsyncEventObserver::VsyncEventObserver(PhysicalDevice& disp) + : mLock(), + mCondition(), + mDisplayDevice(disp), + mVsyncControl(NULL), + mDevice(IDisplayDevice::DEVICE_COUNT), + mEnabled(false), + mExitThread(false), + mInitialized(false) +{ + CTRACE(); +} + +VsyncEventObserver::~VsyncEventObserver() +{ + WARN_IF_NOT_DEINIT(); +} + +bool VsyncEventObserver::initialize() +{ + if (mInitialized) { + WTRACE("object has been initialized"); + return true; + } + + mExitThread = false; + mEnabled = false; + mDevice = mDisplayDevice.getType(); + mVsyncControl = mDisplayDevice.createVsyncControl(); + if (!mVsyncControl || !mVsyncControl->initialize()) { + DEINIT_AND_RETURN_FALSE("failed to initialize vsync control"); + } + + mThread = new VsyncEventPollThread(this); + if (!mThread.get()) { + DEINIT_AND_RETURN_FALSE("failed to create vsync event poll thread."); + } + + mThread->run("VsyncEventObserver", PRIORITY_URGENT_DISPLAY); + + mInitialized = true; + return true; +} + +void VsyncEventObserver::deinitialize() +{ + if (mEnabled) { + WTRACE("vsync is still enabled"); + control(false); + } + mInitialized = false; + mExitThread = true; + mEnabled = false; + mCondition.signal(); + + if (mThread.get()) { + mThread->requestExitAndWait(); + mThread = NULL; + } + + DEINIT_AND_DELETE_OBJ(mVsyncControl); +} + +bool VsyncEventObserver::control(bool enabled) +{ + ATRACE("enabled = %d on device %d", enabled, mDevice); + if (enabled == mEnabled) { + WTRACE("vsync state %d is not changed", enabled); + return true; + } + + Mutex::Autolock _l(mLock); + bool ret = mVsyncControl->control(mDevice, enabled); + if (!ret) { + ETRACE("failed to control (%d) vsync on display %d", enabled, mDevice); + return false; + } + + mEnabled = enabled; + mCondition.signal(); + return true; +} + +bool VsyncEventObserver::threadLoop() +{ + do { + // scope for lock + Mutex::Autolock _l(mLock); + while (!mEnabled) { + mCondition.wait(mLock); + if (mExitThread) { + ITRACE("exiting thread loop"); + return false; + } + } + } while (0); + + if(mEnabled && mDisplayDevice.isConnected()) { + int64_t timestamp; + bool ret = mVsyncControl->wait(mDevice, timestamp); + if (ret == false) { + WTRACE("failed to wait for vsync on display %d, vsync enabled %d", mDevice, mEnabled); + usleep(16000); + return true; + } + + // notify device + mDisplayDevice.onVsync(timestamp); + } + + return true; +} + +} // namespace intel +} // namesapce android diff --git a/merrifield/common/observers/VsyncEventObserver.h b/merrifield/common/observers/VsyncEventObserver.h new file mode 100644 index 0000000..36cb99f --- /dev/null +++ b/merrifield/common/observers/VsyncEventObserver.h @@ -0,0 +1,56 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef __VSYNC_EVENT_OBSERVER_H__ +#define __VSYNC_EVENT_OBSERVER_H__ + +#include <SimpleThread.h> +#include <IVsyncControl.h> + +namespace android { +namespace intel { + +class PhysicalDevice; + +class VsyncEventObserver { +public: + VsyncEventObserver(PhysicalDevice& disp); + virtual ~VsyncEventObserver(); + +public: + virtual bool initialize(); + virtual void deinitialize(); + bool control(bool enabled); + +private: + mutable Mutex mLock; + Condition mCondition; + PhysicalDevice& mDisplayDevice; + IVsyncControl *mVsyncControl; + int mDevice; + bool mEnabled; + bool mExitThread; + bool mInitialized; + +private: + DECLARE_THREAD(VsyncEventPollThread, VsyncEventObserver); +}; + +} // namespace intel +} // namespace android + + + +#endif /* __VSYNC_EVENT_OBSERVER_H__ */ diff --git a/merrifield/common/planes/DisplayPlane.cpp b/merrifield/common/planes/DisplayPlane.cpp new file mode 100644 index 0000000..2a1ca51 --- /dev/null +++ b/merrifield/common/planes/DisplayPlane.cpp @@ -0,0 +1,384 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <HwcTrace.h> +#include <Hwcomposer.h> +#include <DisplayPlane.h> +#include <GraphicBuffer.h> + +namespace android { +namespace intel { + +DisplayPlane::DisplayPlane(int index, int type, int disp) + : mIndex(index), + mType(type), + mZOrder(-1), + mDevice(disp), + mInitialized(false), + mDataBuffers(), + mActiveBuffers(), + mCacheCapacity(0), + mIsProtectedBuffer(false), + mTransform(0), + mPlaneAlpha(0), + mBlending(HWC_BLENDING_NONE), + mCurrentDataBuffer(0), + mUpdateMasks(0) +{ + CTRACE(); + memset(&mPosition, 0, sizeof(mPosition)); + memset(&mSrcCrop, 0, sizeof(mSrcCrop)); +} + +DisplayPlane::~DisplayPlane() +{ + WARN_IF_NOT_DEINIT(); +} + +bool DisplayPlane::initialize(uint32_t bufferCount) +{ + CTRACE(); + + if (bufferCount < MIN_DATA_BUFFER_COUNT) { + WTRACE("buffer count %d is too small", bufferCount); + bufferCount = MIN_DATA_BUFFER_COUNT; + } + + // create buffer cache, adding few extra slots as buffer rendering is async + // buffer could still be queued in the display pipeline such that they + // can't be unmapped] + mCacheCapacity = bufferCount; + mDataBuffers.setCapacity(bufferCount); + mActiveBuffers.setCapacity(MIN_DATA_BUFFER_COUNT); + mInitialized = true; + return true; +} + +void DisplayPlane::deinitialize() +{ + // invalidate cached data buffers + if (mDataBuffers.size()) { + // invalidateBufferCache will assert if object is not initialized + // so invoking it only there is buffer to invalidate. + invalidateBufferCache(); + } + + // invalidate active buffers + if (mActiveBuffers.size()) { + invalidateActiveBuffers(); + } + + mCurrentDataBuffer = 0; + mInitialized = false; +} + +void DisplayPlane::checkPosition(int& x, int& y, int& w, int& h) +{ + drmModeModeInfoPtr mode = &mModeInfo; + + if (mode->hdisplay == 0 || mode->vdisplay == 0) + return; + + if (x < 0) + x = 0; + if (y < 0) + y = 0; + if ((x + w) > mode->hdisplay) + w = mode->hdisplay - x; + if ((y + h) > mode->vdisplay) + h = mode->vdisplay - y; +} + +void DisplayPlane::setPosition(int x, int y, int w, int h) +{ + ATRACE("Position = %d, %d - %dx%d", x, y, w, h); + + if (mPosition.x != x || mPosition.y != y || + mPosition.w != w || mPosition.h != h) { + mUpdateMasks |= PLANE_POSITION_CHANGED; + mPosition.x = x; + mPosition.y = y; + mPosition.w = w; + mPosition.h = h; + } +} + +void DisplayPlane::setSourceCrop(int x, int y, int w, int h) +{ + ATRACE("Source crop = %d, %d - %dx%d", x, y, w, h); + + if (mSrcCrop.x != x || mSrcCrop.y != y || + mSrcCrop.w != w || mSrcCrop.h != h) { + mUpdateMasks |= PLANE_SOURCE_CROP_CHANGED; + mSrcCrop.x = x; + mSrcCrop.y = y; + if (mType == DisplayPlane::PLANE_OVERLAY) { + mSrcCrop.w = w & (~0x01); + mSrcCrop.h = h & (~0x01); + } else { + mSrcCrop.w = w; + mSrcCrop.h = h; + } + } +} + +void DisplayPlane::setTransform(int trans) +{ + ATRACE("transform = %d", trans); + + if (mTransform == trans) { + return; + } + + mTransform = trans; + + mUpdateMasks |= PLANE_TRANSFORM_CHANGED; +} + +void DisplayPlane::setPlaneAlpha(uint8_t alpha, uint32_t blending) +{ + ATRACE("plane alpha = 0x%x", alpha); + + if (mPlaneAlpha != alpha) { + mPlaneAlpha = alpha; + mUpdateMasks |= PLANE_BUFFER_CHANGED; + } + + if (mBlending != blending) { + mBlending = blending; + mUpdateMasks |= PLANE_BUFFER_CHANGED; + } +} + +bool DisplayPlane::setDataBuffer(buffer_handle_t handle) +{ + DataBuffer *buffer; + BufferMapper *mapper; + ssize_t index; + bool ret; + bool isCompression; + BufferManager *bm = Hwcomposer::getInstance().getBufferManager(); + + RETURN_FALSE_IF_NOT_INIT(); + ATRACE("handle = %#x", handle); + + if (!handle) { + WTRACE("invalid buffer handle"); + return false; + } + + // do not need to update the buffer handle + if (mCurrentDataBuffer != handle) + mUpdateMasks |= PLANE_BUFFER_CHANGED; + + // if no update then do Not need set data buffer + if (!mUpdateMasks) + return true; + + buffer = bm->lockDataBuffer(handle); + if (!buffer) { + ETRACE("failed to get buffer"); + return false; + } + + mIsProtectedBuffer = GraphicBuffer::isProtectedBuffer((GraphicBuffer*)buffer); + isCompression = GraphicBuffer::isCompressionBuffer((GraphicBuffer*)buffer); + + // map buffer if it's not in cache + index = mDataBuffers.indexOfKey(buffer->getKey()); + if (index < 0) { + VTRACE("unmapped buffer, mapping..."); + mapper = mapBuffer(buffer); + if (!mapper) { + ETRACE("failed to map buffer %p", handle); + bm->unlockDataBuffer(buffer); + return false; + } + } else { + VTRACE("got mapper in saved data buffers and update source Crop"); + mapper = mDataBuffers.valueAt(index); + } + + // always update source crop to mapper + mapper->setCrop(mSrcCrop.x, mSrcCrop.y, mSrcCrop.w, mSrcCrop.h); + + mapper->setIsCompression(isCompression); + + // unlock buffer after getting mapper + bm->unlockDataBuffer(buffer); + buffer = NULL; + + ret = setDataBuffer(*mapper); + if (ret) { + mCurrentDataBuffer = handle; + // update active buffers + updateActiveBuffers(mapper); + } + return ret; +} + +BufferMapper* DisplayPlane::mapBuffer(DataBuffer *buffer) +{ + BufferManager *bm = Hwcomposer::getInstance().getBufferManager(); + + // invalidate buffer cache if cache is full + if ((int)mDataBuffers.size() >= mCacheCapacity) { + invalidateBufferCache(); + } + + BufferMapper *mapper = bm->map(*buffer); + if (!mapper) { + ETRACE("failed to map buffer"); + return NULL; + } + + // add it to data buffers + ssize_t index = mDataBuffers.add(buffer->getKey(), mapper); + if (index < 0) { + ETRACE("failed to add mapper"); + bm->unmap(mapper); + return NULL; + } + + return mapper; +} + +int DisplayPlane::findActiveBuffer(BufferMapper *mapper) +{ + for (size_t i = 0; i < mActiveBuffers.size(); i++) { + BufferMapper *activeMapper = mActiveBuffers.itemAt(i); + if (!activeMapper) + continue; + if (activeMapper->getKey() == mapper->getKey()) + return i; + } + + return -1; +} + +void DisplayPlane::updateActiveBuffers(BufferMapper *mapper) +{ + BufferManager *bm = Hwcomposer::getInstance().getBufferManager(); + int index = findActiveBuffer(mapper); + bool exist = (0 <= index && index < (int)mActiveBuffers.size()); + + // unmap the first entry (oldest buffer) + if (!exist && mActiveBuffers.size() >= MIN_DATA_BUFFER_COUNT) { + BufferMapper *oldest = mActiveBuffers.itemAt(0); + bm->unmap(oldest); + mActiveBuffers.removeAt(0); + } + + // queue it to active buffers + if (!exist) { + mapper->incRef(); + } else { + mActiveBuffers.removeAt(index); + } + mActiveBuffers.push_back(mapper); +} + +void DisplayPlane::invalidateActiveBuffers() +{ + BufferManager *bm = Hwcomposer::getInstance().getBufferManager(); + BufferMapper* mapper; + + RETURN_VOID_IF_NOT_INIT(); + + VTRACE("invalidating active buffers"); + + for (size_t i = 0; i < mActiveBuffers.size(); i++) { + mapper = mActiveBuffers.itemAt(i); + // unmap it + bm->unmap(mapper); + } + + // clear recorded data buffers + mActiveBuffers.clear(); +} + +void DisplayPlane::invalidateBufferCache() +{ + BufferManager *bm = Hwcomposer::getInstance().getBufferManager(); + BufferMapper* mapper; + + RETURN_VOID_IF_NOT_INIT(); + + for (size_t i = 0; i < mDataBuffers.size(); i++) { + mapper = mDataBuffers.valueAt(i); + bm->unmap(mapper); + } + + mDataBuffers.clear(); + // reset current buffer + mCurrentDataBuffer = 0; +} + +bool DisplayPlane::assignToDevice(int disp) +{ + RETURN_FALSE_IF_NOT_INIT(); + ATRACE("disp = %d", disp); + + mDevice = disp; + + Drm *drm = Hwcomposer::getInstance().getDrm(); + if (!drm->getModeInfo(mDevice, mModeInfo)) { + ETRACE("failed to get mode info"); + } + + mPanelOrientation = drm->getPanelOrientation(mDevice); + + return true; +} + +bool DisplayPlane::flip(void *ctx) +{ + RETURN_FALSE_IF_NOT_INIT(); + + // always flip + return true; +} + +void DisplayPlane::postFlip() +{ + mUpdateMasks = 0; +} + +bool DisplayPlane::reset() +{ + // reclaim all allocated resources + if (mDataBuffers.size() > 0) { + invalidateBufferCache(); + } + + if (mActiveBuffers.size() > 0) { + invalidateActiveBuffers(); + } + + return true; +} + +void DisplayPlane::setZOrder(int zorder) +{ + mZOrder = zorder; +} + +int DisplayPlane::getZOrder() const +{ + return mZOrder; +} + +} // namespace intel +} // namespace android diff --git a/merrifield/common/planes/DisplayPlaneManager.cpp b/merrifield/common/planes/DisplayPlaneManager.cpp new file mode 100644 index 0000000..1b60d93 --- /dev/null +++ b/merrifield/common/planes/DisplayPlaneManager.cpp @@ -0,0 +1,350 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <HwcTrace.h> +#include <IDisplayDevice.h> +#include <DisplayPlaneManager.h> + +namespace android { +namespace intel { + +DisplayPlaneManager::DisplayPlaneManager() + : mTotalPlaneCount(0), + mPrimaryPlaneCount(DEFAULT_PRIMARY_PLANE_COUNT), + mSpritePlaneCount(0), + mOverlayPlaneCount(0), + mInitialized(false) +{ + int i; + + for (i = 0; i < DisplayPlane::PLANE_MAX; i++) { + mPlaneCount[i] = 0; + mFreePlanes[i] = 0; + mReclaimedPlanes[i] = 0; + } +} + +DisplayPlaneManager::~DisplayPlaneManager() +{ + WARN_IF_NOT_DEINIT(); +} + +void DisplayPlaneManager::deinitialize() +{ + int i; + size_t j; + + for (i = 0; i < DisplayPlane::PLANE_MAX; i++) { + for (j = 0; j < mPlanes[i].size(); j++) { + // reset plane + DisplayPlane *plane = mPlanes[i].itemAt(j); + plane->reset(); + + DEINIT_AND_DELETE_OBJ(plane); + } + mPlanes[i].clear(); + } + + mInitialized = false; +} + +bool DisplayPlaneManager::initialize() +{ + int i, j; + + if (mInitialized) { + WTRACE("object has been initialized"); + return true; + } + + + // calculate total plane number and free plane bitmaps + mPlaneCount[DisplayPlane::PLANE_SPRITE] = mSpritePlaneCount; + mPlaneCount[DisplayPlane::PLANE_OVERLAY] = mOverlayPlaneCount; + mPlaneCount[DisplayPlane::PLANE_PRIMARY] = mPrimaryPlaneCount; + mPlaneCount[DisplayPlane::PLANE_CURSOR] = mCursorPlaneCount; + + mTotalPlaneCount = mSpritePlaneCount+ mOverlayPlaneCount+ mPrimaryPlaneCount + mCursorPlaneCount; + if (mTotalPlaneCount == 0) { + ETRACE("plane count is not initialized"); + return false; + } + + for (i = 0; i < DisplayPlane::PLANE_MAX; i++) { + mFreePlanes[i] = ((1 << mPlaneCount[i]) - 1); + } + + // allocate plane pools + for (i = 0; i < DisplayPlane::PLANE_MAX; i++) { + if (mPlaneCount[i]) { + mPlanes[i].setCapacity(mPlaneCount[i]); + + for (j = 0; j < mPlaneCount[i]; j++) { + DisplayPlane* plane = allocPlane(j, i); + if (!plane) { + ETRACE("failed to allocate plane %d, type %d", j, i); + DEINIT_AND_RETURN_FALSE(); + } + mPlanes[i].push_back(plane); + } + } + } + + mInitialized = true; + return true; +} + +int DisplayPlaneManager::getPlane(uint32_t& mask) +{ + if (!mask) + return -1; + + for (int i = 0; i < 32; i++) { + int bit = (1 << i); + if (bit & mask) { + mask &= ~bit; + return i; + } + } + + return -1; +} + +void DisplayPlaneManager::putPlane(int index, uint32_t& mask) +{ + if (index < 0 || index >= 32) + return; + + int bit = (1 << index); + + if (bit & mask) { + WTRACE("bit %d was set", index); + return; + } + + mask |= bit; +} + +int DisplayPlaneManager::getPlane(uint32_t& mask, int index) +{ + if (!mask || index < 0 || index > mTotalPlaneCount) + return -1; + + int bit = (1 << index); + if (bit & mask) { + mask &= ~bit; + return index; + } + + return -1; +} + +DisplayPlane* DisplayPlaneManager::getPlane(int type, int index) +{ + RETURN_NULL_IF_NOT_INIT(); + + if (type < 0 || type >= DisplayPlane::PLANE_MAX) { + ETRACE("Invalid plane type %d", type); + return 0; + } + + int freePlaneIndex = getPlane(mReclaimedPlanes[type], index); + if (freePlaneIndex >= 0) + return mPlanes[type].itemAt(freePlaneIndex); + + freePlaneIndex = getPlane(mFreePlanes[type], index); + if (freePlaneIndex >= 0) + return mPlanes[type].itemAt(freePlaneIndex); + + return 0; +} + +DisplayPlane* DisplayPlaneManager::getAnyPlane(int type) +{ + RETURN_NULL_IF_NOT_INIT(); + + if (type < 0 || type >= DisplayPlane::PLANE_MAX) { + ETRACE("Invalid plane type %d", type); + return 0; + } + + int freePlaneIndex = getPlane(mReclaimedPlanes[type]); + if (freePlaneIndex >= 0) + return mPlanes[type].itemAt(freePlaneIndex); + + freePlaneIndex = getPlane(mFreePlanes[type]); + if (freePlaneIndex >= 0) + return mPlanes[type].itemAt(freePlaneIndex); + + return 0; +} + +void DisplayPlaneManager::putPlane(int dsp, DisplayPlane& plane) +{ + int index; + int type; + + RETURN_VOID_IF_NOT_INIT(); + + index = plane.getIndex(); + type = plane.getType(); + + if (type < 0 || type >= DisplayPlane::PLANE_MAX) { + ETRACE("Invalid plane type %d", type); + return; + } + + putPlane(index, mFreePlanes[type]); +} + +bool DisplayPlaneManager::isFreePlane(int type, int index) +{ + if (type < 0 || type >= DisplayPlane::PLANE_MAX) { + ETRACE("Invalid plane type %d", type); + return false; + } + + int freePlanes = mFreePlanes[type] | mReclaimedPlanes[type]; + if ((freePlanes & (1 << index)) == 0) + return false; + + return true; +} + +int DisplayPlaneManager::getFreePlanes(int dsp, int type) +{ + RETURN_NULL_IF_NOT_INIT(); + + if (dsp < 0 || dsp > IDisplayDevice::DEVICE_EXTERNAL) { + ETRACE("Invalid display device %d", dsp); + return 0; + } + + if (type < 0 || type >= DisplayPlane::PLANE_MAX) { + ETRACE("Invalid plane type %d", type); + return 0; + } + + + uint32_t freePlanes = mFreePlanes[type] | mReclaimedPlanes[type]; + if (type == DisplayPlane::PLANE_PRIMARY || + type == DisplayPlane::PLANE_CURSOR) { + return ((freePlanes & (1 << dsp)) == 0) ? 0 : 1; + } else { + int count = 0; + for (int i = 0; i < 32; i++) { + if ((1 << i) & freePlanes) { + count++; + } + } + return count; + } + return 0; +} + +void DisplayPlaneManager::reclaimPlane(int dsp, DisplayPlane& plane) +{ + RETURN_VOID_IF_NOT_INIT(); + + int index = plane.getIndex(); + int type = plane.getType(); + + ATRACE("reclaimPlane = %d, type = %d", index, plane.getType()); + + if (type < 0 || type >= DisplayPlane::PLANE_MAX) { + ETRACE("Invalid plane type %d", type); + return; + } + + putPlane(index, mReclaimedPlanes[type]); + + // NOTE: don't invalidate plane's data cache here because the reclaimed + // plane might be re-assigned to the same layer later +} + +void DisplayPlaneManager::disableReclaimedPlanes() +{ + int i, j; + bool ret; + + RETURN_VOID_IF_NOT_INIT(); + + for (i = 0; i < DisplayPlane::PLANE_MAX; i++) { + // disable reclaimed planes + if (mReclaimedPlanes[i]) { + for (j = 0; j < mPlaneCount[i]; j++) { + int bit = (1 << j); + if (mReclaimedPlanes[i] & bit) { + DisplayPlane* plane = mPlanes[i].itemAt(j); + // check plane state first + ret = plane->isDisabled(); + // reset plane + if (ret) + ret = plane->reset(); + if (ret) { + // only merge into free bitmap if it is successfully disabled and reset + // otherwise, plane will be disabled and reset again. + mFreePlanes[i] |=bit; + mReclaimedPlanes[i] &= ~bit; + } + } + } + } + } +} + +bool DisplayPlaneManager::isOverlayPlanesDisabled() +{ + for (int i = 0; i < DisplayPlane::PLANE_MAX; i++) { + for (int j = 0; j < mPlaneCount[i]; j++) { + DisplayPlane* plane = (DisplayPlane *)mPlanes[i][j]; + if (plane && plane->getType() == DisplayPlane::PLANE_OVERLAY) { + if (!plane->isDisabled()) + return false; + } + } + } + + return true; +} + +void DisplayPlaneManager::dump(Dump& d) +{ + d.append("Display Plane Manager state:\n"); + d.append("-------------------------------------------------------------\n"); + d.append(" PLANE TYPE | COUNT | FREE | RECLAIMED \n"); + d.append("------------+-------+----------+-----------\n"); + d.append(" SPRITE | %2d | %08x | %08x\n", + mPlaneCount[DisplayPlane::PLANE_SPRITE], + mFreePlanes[DisplayPlane::PLANE_SPRITE], + mReclaimedPlanes[DisplayPlane::PLANE_SPRITE]); + d.append(" OVERLAY | %2d | %08x | %08x\n", + mPlaneCount[DisplayPlane::PLANE_OVERLAY], + mFreePlanes[DisplayPlane::PLANE_OVERLAY], + mReclaimedPlanes[DisplayPlane::PLANE_OVERLAY]); + d.append(" PRIMARY | %2d | %08x | %08x\n", + mPlaneCount[DisplayPlane::PLANE_PRIMARY], + mFreePlanes[DisplayPlane::PLANE_PRIMARY], + mReclaimedPlanes[DisplayPlane::PLANE_PRIMARY]); + d.append(" CURSOR | %2d | %08x | %08x\n", + mPlaneCount[DisplayPlane::PLANE_CURSOR], + mFreePlanes[DisplayPlane::PLANE_CURSOR], + mReclaimedPlanes[DisplayPlane::PLANE_CURSOR]); +} + +} // namespace intel +} // namespace android + + diff --git a/merrifield/common/utils/Dump.cpp b/merrifield/common/utils/Dump.cpp new file mode 100644 index 0000000..31d924f --- /dev/null +++ b/merrifield/common/utils/Dump.cpp @@ -0,0 +1,53 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <stdarg.h> +#include <stdio.h> + +#include <Dump.h> + +namespace android { +namespace intel { + +Dump::Dump(char *buf, int len) + : mBuf(buf), + mLen(len) +{ + +} + +Dump::~Dump() +{ + +} + +void Dump::append(const char *fmt, ...) +{ + int len; + + if (!mBuf || !mLen) + return; + + va_list ap; + va_start(ap, fmt); + len = vsnprintf(mBuf, mLen, fmt, ap); + va_end(ap); + + mLen -= len; + mBuf += len; +} + +} // namespace intel +} // namespace android diff --git a/merrifield/common/utils/Dump.h b/merrifield/common/utils/Dump.h new file mode 100644 index 0000000..ba996b2 --- /dev/null +++ b/merrifield/common/utils/Dump.h @@ -0,0 +1,35 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef DUMP_H_ +#define DUMP_H_ + +namespace android { +namespace intel { + +class Dump { +public: + Dump(char *buf, int len); + ~Dump(); + + void append(const char *fmt, ...); +private: + char *mBuf; + int mLen; +}; + +} // namespace intel +} // namespace android +#endif /* DUMP_H_ */ diff --git a/merrifield/common/utils/HwcTrace.h b/merrifield/common/utils/HwcTrace.h new file mode 100644 index 0000000..83151c6 --- /dev/null +++ b/merrifield/common/utils/HwcTrace.h @@ -0,0 +1,112 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef HWC_TRACE_H +#define HWC_TRACE_H + +#define LOG_TAG "hwcomposer" +//#define LOG_NDEBUG 0 +#include <cutils/log.h> + + +#ifdef _cplusplus +extern "C" { +#endif + +// Helper to automatically preappend classname::functionname to the log message +#define VTRACE(fmt,...) ALOGV("%s: "fmt, __func__, ##__VA_ARGS__) +#define DTRACE(fmt,...) ALOGD("%s: "fmt, __func__, ##__VA_ARGS__) +#define ITRACE(fmt,...) ALOGI("%s: "fmt, __func__, ##__VA_ARGS__) +#define WTRACE(fmt,...) ALOGW("%s: "fmt, __func__, ##__VA_ARGS__) +#define ETRACE(fmt,...) ALOGE("%s: "fmt, __func__, ##__VA_ARGS__) + + +// Function call tracing +#if 0 +#define CTRACE() ALOGV("Calling %s", __func__) +#define XTRACE() ALOGV("Leaving %s", __func__) +#else +#define CTRACE() ((void)0) +#define XTRACE() ((void)0) +#endif + + +// Arguments tracing +#if 0 +#define ATRACE(fmt,...) ALOGV("%s(args): "fmt, __func__, ##__VA_ARGS__); +#else +#define ATRACE(fmt,...) ((void)0) +#endif + + + +// Helper to abort the execution if object is not initialized. +// This should never happen if the rules below are followed during design: +// 1) Create an object. +// 2) Initialize the object immediately. +// 3) If failed, delete the object. +// These helpers should be disabled and stripped out of release build + +#define RETURN_X_IF_NOT_INIT(X) \ +do { \ + CTRACE(); \ + if (false == mInitialized) { \ + LOG_ALWAYS_FATAL("%s: Object is not initialized! Line = %d", __func__, __LINE__); \ + return X; \ + } \ +} while (0) + +#if 1 +#define RETURN_FALSE_IF_NOT_INIT() RETURN_X_IF_NOT_INIT(false) +#define RETURN_VOID_IF_NOT_INIT() RETURN_X_IF_NOT_INIT() +#define RETURN_NULL_IF_NOT_INIT() RETURN_X_IF_NOT_INIT(0) +#else +#define RETURN_FALSE_IF_NOT_INIT() ((void)0) +#define RETURN_VOID_IF_NOT_INIT() ((void)0) +#define RETURN_NULL_IF_NOT_INIT() ((void)0) +#endif + + +// Helper to log error message, call de-initializer and return false. +#define DEINIT_AND_RETURN_FALSE(...) \ +do { \ + ETRACE(__VA_ARGS__); \ + deinitialize(); \ + return false; \ +} while (0) + + +#define DEINIT_AND_DELETE_OBJ(X) \ + if (X) {\ + X->deinitialize();\ + delete X; \ + X = NULL; \ + } + + +#define WARN_IF_NOT_DEINIT() \ + CTRACE(); \ + if (mInitialized) {\ + LOG_ALWAYS_FATAL("%s: Object is not deinitialized! Line = %d", __func__, __LINE__); \ + } + + +// _cplusplus +#ifdef _cplusplus +} +#endif + + +#endif /* HWC_TRACE_H */ diff --git a/merrifield/include/BufferManager.h b/merrifield/include/BufferManager.h new file mode 100644 index 0000000..ccc8eaa --- /dev/null +++ b/merrifield/include/BufferManager.h @@ -0,0 +1,89 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef BUFFERMANAGER_H_ +#define BUFFERMANAGER_H_ + +#include <Dump.h> +#include <DataBuffer.h> +#include <BufferMapper.h> +#include <BufferCache.h> +#include <utils/Mutex.h> + +namespace android { +namespace intel { + +// Gralloc Buffer Manager +class BufferManager { +public: + BufferManager(); + virtual ~BufferManager(); + + bool initCheck() const; + virtual bool initialize(); + virtual void deinitialize(); + + // dump interface + void dump(Dump& d); + + // lockDataBuffer and unlockDataBuffer must be used in serial + // nested calling of them will cause a deadlock + DataBuffer* lockDataBuffer(buffer_handle_t handle); + void unlockDataBuffer(DataBuffer *buffer); + + // get and put interfaces are deprecated + // use lockDataBuffer and unlockDataBuffer instead + DataBuffer* get(buffer_handle_t handle); + void put(DataBuffer *buffer); + + // map/unmap a data buffer into/from display memory + BufferMapper* map(DataBuffer& buffer); + void unmap(BufferMapper *mapper); + + // frame buffer management + //return 0 if allocation fails + virtual buffer_handle_t allocFrameBuffer(int width, int height, int *stride); + virtual void freeFrameBuffer(buffer_handle_t fbHandle); + + buffer_handle_t allocGrallocBuffer(uint32_t width, uint32_t height, uint32_t format, uint32_t usage); + void freeGrallocBuffer(buffer_handle_t handle); + virtual bool blit(buffer_handle_t srcHandle, buffer_handle_t destHandle, + const crop_t& destRect, bool filter, bool async) = 0; +protected: + virtual DataBuffer* createDataBuffer(gralloc_module_t *module, + buffer_handle_t handle) = 0; + virtual BufferMapper* createBufferMapper(gralloc_module_t *module, + DataBuffer& buffer) = 0; + + gralloc_module_t *mGrallocModule; +private: + enum { + // make the buffer pool large enough + DEFAULT_BUFFER_POOL_SIZE = 128, + }; + + alloc_device_t *mAllocDev; + KeyedVector<buffer_handle_t, BufferMapper*> mFrameBuffers; + BufferCache *mBufferPool; + DataBuffer *mDataBuffer; + Mutex mDataBufferLock; + Mutex mLock; + bool mInitialized; +}; + +} // namespace intel +} // namespace android + +#endif /* BUFFERMANAGER_H_ */ diff --git a/merrifield/include/BufferMapper.h b/merrifield/include/BufferMapper.h new file mode 100644 index 0000000..7a4ceaf --- /dev/null +++ b/merrifield/include/BufferMapper.h @@ -0,0 +1,68 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef BUFFERMAPPER_H__ +#define BUFFERMAPPER_H__ + +#include <DataBuffer.h> + +namespace android { +namespace intel { + +class BufferMapper : public DataBuffer { +public: + BufferMapper(DataBuffer& buffer) + : DataBuffer(buffer), + mRefCount(0) + { + } + virtual ~BufferMapper() {} +public: + int incRef() + { + mRefCount++; + return mRefCount; + } + int decRef() + { + mRefCount--; + return mRefCount; + } + + int getRef() const + { + return mRefCount; + } + + // map the given buffer into both DC & CPU MMU + virtual bool map() = 0; + // unmap the give buffer from both DC & CPU MMU + virtual bool unmap() = 0; + + // return gtt page offset + virtual uint32_t getGttOffsetInPage(int subIndex) const = 0; + virtual void* getCpuAddress(int subIndex) const = 0; + virtual uint32_t getSize(int subIndex) const = 0; + virtual buffer_handle_t getKHandle(int subIndex) = 0; + virtual buffer_handle_t getFbHandle(int subIndex) = 0; + virtual void putFbHandle() = 0; +private: + int mRefCount; +}; + +} // namespace intel +} // namespace android + +#endif /* BUFFERMAPPER_H__ */ diff --git a/merrifield/include/DataBuffer.h b/merrifield/include/DataBuffer.h new file mode 100644 index 0000000..a4a6d84 --- /dev/null +++ b/merrifield/include/DataBuffer.h @@ -0,0 +1,114 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef DATABUFFER_H__ +#define DATABUFFER_H__ + +#include <hardware/hwcomposer.h> + +namespace android { +namespace intel { + +typedef struct crop { + // align with android, using 'int' here + int x; + int y; + int w; + int h; +} crop_t; + +typedef struct stride { + union { + struct { + uint32_t stride; + } rgb; + struct { + uint32_t yStride; + uint32_t uvStride; + } yuv; + }; +}stride_t; + +class DataBuffer { +public: + enum { + FORMAT_INVALID = 0xffffffff, + }; +public: + DataBuffer(buffer_handle_t handle) + { + initBuffer(handle); + } + virtual ~DataBuffer() {} + +public: + virtual void resetBuffer(buffer_handle_t handle) { + // nothing to reset, just do initialization + initBuffer(handle); + } + + buffer_handle_t getHandle() const { return mHandle; } + + void setStride(stride_t& stride) { mStride = stride; } + stride_t& getStride() { return mStride; } + + void setWidth(uint32_t width) { mWidth = width; } + uint32_t getWidth() const { return mWidth; } + + void setHeight(uint32_t height) { mHeight = height; } + uint32_t getHeight() const { return mHeight; } + + void setCrop(int x, int y, int w, int h) { + mCrop.x = x; mCrop.y = y; mCrop.w = w; mCrop.h = h; } + crop_t& getCrop() { return mCrop; } + + void setFormat(uint32_t format) { mFormat = format; } + uint32_t getFormat() const { return mFormat; } + + uint64_t getKey() const { return mKey; } + + void setIsCompression(bool isCompressed) { mIsCompression = isCompressed; } + bool isCompression() { return mIsCompression; } + +private: + void initBuffer(buffer_handle_t handle) { + mHandle = handle; + mFormat = 0; + mWidth = 0; + mHeight = 0; + mKey = (uint64_t)handle; + memset(&mStride, 0, sizeof(stride_t)); + memset(&mCrop, 0, sizeof(crop_t)); + } +protected: + buffer_handle_t mHandle; + stride_t mStride; + crop_t mCrop; + uint32_t mFormat; + uint32_t mWidth; + uint32_t mHeight; + uint64_t mKey; + bool mIsCompression; +}; + +static inline uint32_t align_to(uint32_t arg, uint32_t align) +{ + return ((arg + (align - 1)) & (~(align - 1))); +} + +} // namespace intel +} // namespace android + +#endif /* DATABUFFER_H__ */ diff --git a/merrifield/include/DisplayPlane.h b/merrifield/include/DisplayPlane.h new file mode 100644 index 0000000..250d485 --- /dev/null +++ b/merrifield/include/DisplayPlane.h @@ -0,0 +1,154 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef DISPLAYPLANE_H_ +#define DISPLAYPLANE_H_ + +#include <utils/KeyedVector.h> +#include <BufferMapper.h> +#include <Drm.h> + +namespace android { +namespace intel { + +typedef struct { + // align with android, using 'int' here + int x; + int y; + int w; + int h; +} PlanePosition; + +enum { + // support up to 4 overlays + MAX_OVERLAY_COUNT = 4, + MAX_SPRITE_COUNT = 4, +}; + +enum { + // in version 1.3, HWC_FRAMEBUFFER_TARGET is defined as 3 + HWC_FORCE_FRAMEBUFFER = 255, +}; + +class ZOrderConfig; + +class DisplayPlane { +public: + // plane type + enum { + PLANE_SPRITE = 0, + PLANE_OVERLAY, + PLANE_PRIMARY, + PLANE_CURSOR, + PLANE_MAX, + }; + + enum { + // one more than android's back buffer count to allow more space + // to do map/unmap, as plane reallocation may unmap on-screen layer. + // each plane will cache the latest MIN_DATA_BUFFER_COUNT buffers + // in case that these buffers are still in-using by display device + // other buffers will be released on cache invalidation + MIN_DATA_BUFFER_COUNT = 4, + }; + +protected: + enum { + PLANE_POSITION_CHANGED = 0x00000001UL, + PLANE_BUFFER_CHANGED = 0x00000002UL, + PLANE_SOURCE_CROP_CHANGED = 0x00000004UL, + PLANE_TRANSFORM_CHANGED = 0x00000008UL, + }; +public: + DisplayPlane(int index, int type, int disp); + virtual ~DisplayPlane(); +public: + virtual int getIndex() const { return mIndex; } + virtual int getType() const { return mType; } + virtual bool initCheck() const { return mInitialized; } + + // data destination + virtual void setPosition(int x, int y, int w, int h); + virtual void setSourceCrop(int x, int y, int w, int h); + virtual void setTransform(int transform); + virtual void setPlaneAlpha(uint8_t alpha, uint32_t blending); + + // data source + virtual bool setDataBuffer(buffer_handle_t handle); + + virtual void invalidateBufferCache(); + + // display device + virtual bool assignToDevice(int disp); + + // hardware operations + virtual bool flip(void *ctx); + virtual void postFlip(); + + virtual bool reset(); + virtual bool enable() = 0; + virtual bool disable() = 0; + virtual bool isDisabled() = 0; + + // set z order config + virtual void setZOrderConfig(ZOrderConfig& config, + void *nativeConfig) = 0; + + virtual void setZOrder(int zorder); + virtual int getZOrder() const; + + virtual void* getContext() const = 0; + + virtual bool initialize(uint32_t bufferCount); + virtual void deinitialize(); + +protected: + virtual void checkPosition(int& x, int& y, int& w, int& h); + virtual bool setDataBuffer(BufferMapper& mapper) = 0; +private: + inline BufferMapper* mapBuffer(DataBuffer *buffer); + + inline int findActiveBuffer(BufferMapper *mapper); + void updateActiveBuffers(BufferMapper *mapper); + void invalidateActiveBuffers(); +protected: + int mIndex; + int mType; + int mZOrder; + int mDevice; + bool mInitialized; + + // cached data buffers + KeyedVector<uint64_t, BufferMapper*> mDataBuffers; + // holding the most recent buffers + Vector<BufferMapper*> mActiveBuffers; + int mCacheCapacity; + + PlanePosition mPosition; + crop_t mSrcCrop; + bool mIsProtectedBuffer; + int mTransform; + uint8_t mPlaneAlpha; + uint32_t mBlending; + buffer_handle_t mCurrentDataBuffer; + uint32_t mUpdateMasks; + drmModeModeInfo mModeInfo; + int mPanelOrientation; +}; + +} // namespace intel +} // namespace android + +#endif /* DISPLAYPLANE_H_ */ diff --git a/merrifield/include/DisplayPlaneManager.h b/merrifield/include/DisplayPlaneManager.h new file mode 100644 index 0000000..1c55d9f --- /dev/null +++ b/merrifield/include/DisplayPlaneManager.h @@ -0,0 +1,112 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef DISPLAYPLANEMANAGER_H_ +#define DISPLAYPLANEMANAGER_H_ + +#include <Dump.h> +#include <DisplayPlane.h> +#include <HwcLayer.h> +#include <utils/Vector.h> + +namespace android { +namespace intel { + +struct ZOrderLayer +{ + ZOrderLayer() { + memset(this, 0, sizeof(ZOrderLayer)); + } + + inline bool operator<(const ZOrderLayer& rhs) const { + return zorder < rhs.zorder; + } + + int planeType; + int zorder; + DisplayPlane *plane; + HwcLayer *hwcLayer; +}; + +class ZOrderConfig : public SortedVector<ZOrderLayer*> { +public: + ZOrderConfig() {} + + int do_compare(const void* lhs, const void* rhs) const { + const ZOrderLayer *l = *(ZOrderLayer**)lhs; + const ZOrderLayer *r = *(ZOrderLayer**)rhs; + + // sorted from z order 0 to n + return l->zorder - r->zorder; + } +}; + + +class DisplayPlaneManager { +public: + DisplayPlaneManager(); + virtual ~DisplayPlaneManager(); + +public: + virtual bool initialize(); + virtual void deinitialize(); + + virtual bool isValidZOrder(int dsp, ZOrderConfig& config) = 0; + virtual bool assignPlanes(int dsp, ZOrderConfig& config) = 0; + // TODO: remove this API + virtual void* getZOrderConfig() const = 0; + virtual int getFreePlanes(int dsp, int type); + virtual void reclaimPlane(int dsp, DisplayPlane& plane); + virtual void disableReclaimedPlanes(); + virtual bool isOverlayPlanesDisabled(); + // dump interface + virtual void dump(Dump& d); + +protected: + // plane allocation & free + int getPlane(uint32_t& mask); + int getPlane(uint32_t& mask, int index); + DisplayPlane* getPlane(int type, int index); + DisplayPlane* getAnyPlane(int type); + void putPlane(int index, uint32_t& mask); + void putPlane(int dsp, DisplayPlane& plane); + bool isFreePlane(int type, int index); + virtual DisplayPlane* allocPlane(int index, int type) = 0; + +protected: + int mPlaneCount[DisplayPlane::PLANE_MAX]; + int mTotalPlaneCount; + int mPrimaryPlaneCount; + int mSpritePlaneCount; + int mOverlayPlaneCount; + int mCursorPlaneCount; + + Vector<DisplayPlane*> mPlanes[DisplayPlane::PLANE_MAX]; + + // Bitmap of free planes. Bit0 - plane A, bit 1 - plane B, etc. + uint32_t mFreePlanes[DisplayPlane::PLANE_MAX]; + uint32_t mReclaimedPlanes[DisplayPlane::PLANE_MAX]; + + bool mInitialized; + +enum { + DEFAULT_PRIMARY_PLANE_COUNT = 3 +}; +}; + +} // namespace intel +} // namespace android + +#endif /* DISPLAYPLANEMANAGER_H_ */ diff --git a/merrifield/include/DisplayQuery.h b/merrifield/include/DisplayQuery.h new file mode 100644 index 0000000..185a25d --- /dev/null +++ b/merrifield/include/DisplayQuery.h @@ -0,0 +1,33 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef DISPLAY_QUERY_H +#define DISPLAY_QUERY_H + +namespace android { +namespace intel { + +class DisplayQuery +{ +public: + static bool isVideoFormat(uint32_t format); + static int getOverlayLumaStrideAlignment(uint32_t format); + static uint32_t queryNV12Format(); +}; + +} // namespace intel +} // namespace android + +#endif /*DISPLAY_QUERY_H*/ diff --git a/merrifield/include/DrmConfig.h b/merrifield/include/DrmConfig.h new file mode 100644 index 0000000..23ab889 --- /dev/null +++ b/merrifield/include/DrmConfig.h @@ -0,0 +1,44 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef DRM_CONFIG_H +#define DRM_CONFIG_H + +namespace android { +namespace intel { + +#define fourcc_code(a, b, c, d) ((__u32)(a) | ((__u32)(b) << 8) | \ + ((__u32)(c) << 16) | ((__u32)(d) << 24)) +#define DRM_FORMAT_XRGB8888 fourcc_code('X', 'R', '2', '4') /* [31:0] x:R:G:B 8:8:8:8 little endian */ + +class DrmConfig +{ +public: + static const char* getDrmPath(); + static uint32_t getDrmConnector(int device); + static uint32_t getDrmEncoder(int device); + static uint32_t getFrameBufferFormat(); + static uint32_t getFrameBufferDepth(); + static uint32_t getFrameBufferBpp(); + static const char* getUeventEnvelope(); + static const char* getHotplugString(); + static const char* getRepeatedFrameString(); + static uint32_t convertHalFormatToDrmFormat(uint32_t halFormat); +}; + +} // namespace intel +} // namespace android + +#endif /*DRM_CONFIG_H*/ diff --git a/merrifield/include/ExternalDevice.h b/merrifield/include/ExternalDevice.h new file mode 100644 index 0000000..ba22167 --- /dev/null +++ b/merrifield/include/ExternalDevice.h @@ -0,0 +1,65 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef EXTERNAL_DEVICE_H +#define EXTERNAL_DEVICE_H + +#include <PhysicalDevice.h> +#include <IHdcpControl.h> +#include <SimpleThread.h> + +namespace android { +namespace intel { + + +class ExternalDevice : public PhysicalDevice { + +public: + ExternalDevice(Hwcomposer& hwc, DeviceControlFactory* controlFactory); + virtual ~ExternalDevice(); +public: + virtual bool initialize(); + virtual void deinitialize(); + virtual bool setDrmMode(drmModeModeInfo& value); + virtual void setRefreshRate(int hz); + virtual int getActiveConfig(); + virtual bool setActiveConfig(int index); + int getRefreshRate(); + +private: + static void HdcpLinkStatusListener(bool success, void *userData); + void HdcpLinkStatusListener(bool success); + void setDrmMode(); +protected: + IHdcpControl *mHdcpControl; + +private: + static void hotplugEventListener(void *data); + void hotplugListener(); + +private: + Condition mAbortModeSettingCond; + drmModeModeInfo mPendingDrmMode; + bool mHotplugEventPending; + int mExpectedRefreshRate; + +private: + DECLARE_THREAD(ModeSettingThread, ExternalDevice); +}; + +} +} + +#endif /* EXTERNAL_DEVICE_H */ diff --git a/merrifield/include/GraphicBuffer.h b/merrifield/include/GraphicBuffer.h new file mode 100644 index 0000000..65320d8 --- /dev/null +++ b/merrifield/include/GraphicBuffer.h @@ -0,0 +1,57 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef GRAPHIC_BUFFER_H +#define GRAPHIC_BUFFER_H + +#include <DataBuffer.h> + + +namespace android { +namespace intel { + +class GraphicBuffer : public DataBuffer { +public: + enum { + USAGE_INVALID = 0xffffffff, + }; + +public: + GraphicBuffer(buffer_handle_t handle); + virtual ~GraphicBuffer() {} + + virtual void resetBuffer(buffer_handle_t handle); + + uint32_t getUsage() const { return mUsage; } + uint32_t getBpp() const { return mBpp; } + + static bool isProtectedUsage(uint32_t usage); + static bool isProtectedBuffer(GraphicBuffer *buffer); + + static bool isCompressionUsage(uint32_t usage); + static bool isCompressionBuffer(GraphicBuffer *buffer); + +private: + void initBuffer(buffer_handle_t handle); + +protected: + uint32_t mUsage; + uint32_t mBpp; +}; + +} // namespace intel +} // namespace android + +#endif /* GRAPHIC_BUFFER_H */ diff --git a/merrifield/include/Hwcomposer.h b/merrifield/include/Hwcomposer.h new file mode 100644 index 0000000..17e5365 --- /dev/null +++ b/merrifield/include/Hwcomposer.h @@ -0,0 +1,136 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef HWCOMPOSER_H +#define HWCOMPOSER_H + +#include <EGL/egl.h> +#include <hardware/hwcomposer.h> +#include <utils/Vector.h> + +#include <IDisplayDevice.h> +#include <BufferManager.h> +#include <IDisplayContext.h> +#include <Drm.h> +#include <DisplayPlaneManager.h> +#include <DisplayAnalyzer.h> +#include <VsyncManager.h> +#include <MultiDisplayObserver.h> +#include <UeventObserver.h> +#include <IPlatFactory.h> + + +namespace android { +namespace intel { + +class Hwcomposer : public hwc_composer_device_1_t { +public: + virtual ~Hwcomposer(); +public: + // callbacks implementation + virtual bool prepare(size_t numDisplays, + hwc_display_contents_1_t** displays); + virtual bool commit(size_t numDisplays, + hwc_display_contents_1_t** displays); + virtual bool vsyncControl(int disp, int enabled); + virtual bool release(); + virtual bool dump(char *buff, int buff_len, int *cur_len); + virtual void registerProcs(hwc_procs_t const *procs); + + virtual bool blank(int disp, int blank); + virtual bool getDisplayConfigs(int disp, + uint32_t *configs, + size_t *numConfigs); + virtual bool getDisplayAttributes(int disp, + uint32_t config, + const uint32_t *attributes, + int32_t *values); + virtual bool compositionComplete(int disp); + + virtual bool setPowerMode(int disp, int mode); + virtual int getActiveConfig(int disp); + virtual bool setActiveConfig(int disp, int index); + virtual bool setCursorPositionAsync(int disp, int x, int y); + + // callbacks + virtual void vsync(int disp, int64_t timestamp); + virtual void hotplug(int disp, bool connected); + virtual void invalidate(); + + virtual bool initCheck() const; + virtual bool initialize(); + virtual void deinitialize(); + + +public: + Drm* getDrm(); + DisplayPlaneManager* getPlaneManager(); + BufferManager* getBufferManager(); + IDisplayContext* getDisplayContext(); + DisplayAnalyzer* getDisplayAnalyzer(); + VsyncManager* getVsyncManager(); + MultiDisplayObserver* getMultiDisplayObserver(); + IDisplayDevice* getDisplayDevice(int disp); + UeventObserver* getUeventObserver(); + IPlatFactory* getPlatFactory() {return mPlatFactory;} +protected: + Hwcomposer(IPlatFactory *factory); + +public: + static Hwcomposer& getInstance() { + Hwcomposer *instance = sInstance; + if (instance == 0) { + instance = createHwcomposer(); + sInstance = instance; + } + return *sInstance; + } + static void releaseInstance() { + delete sInstance; + sInstance = NULL; + } + // Need to be implemented + static Hwcomposer* createHwcomposer(); + + +private: + hwc_procs_t const *mProcs; + Drm *mDrm; + + // plugin through set + IPlatFactory *mPlatFactory; + VsyncManager *mVsyncManager; + DisplayAnalyzer *mDisplayAnalyzer; + MultiDisplayObserver *mMultiDisplayObserver; + UeventObserver *mUeventObserver; + + // created from IPlatFactory + DisplayPlaneManager *mPlaneManager; + BufferManager *mBufferManager; + IDisplayContext *mDisplayContext; + + Vector<IDisplayDevice*> mDisplayDevices; + + bool mInitialized; + + + + static Hwcomposer *sInstance; +}; + +} // namespace intel +} + +#endif /*HW_COMPOSER_H*/ diff --git a/merrifield/include/IBlankControl.h b/merrifield/include/IBlankControl.h new file mode 100644 index 0000000..7051ce1 --- /dev/null +++ b/merrifield/include/IBlankControl.h @@ -0,0 +1,33 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef IBLANKCONTROL_H_ +#define IBLANKCONTROL_H_ + +namespace android { +namespace intel { + +class IBlankControl { +public: + IBlankControl() {} + virtual ~IBlankControl() {} +public: + virtual bool blank(int disp, bool blank) = 0; +}; + +} // namespace intel +} // namespace android + +#endif /* IBLANKCONTROL_H_ */ diff --git a/merrifield/include/IDisplayContext.h b/merrifield/include/IDisplayContext.h new file mode 100644 index 0000000..7767856 --- /dev/null +++ b/merrifield/include/IDisplayContext.h @@ -0,0 +1,43 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef IDISPLAY_CONTEXT_H +#define IDISPLAY_CONTEXT_H + +#include <hardware/hwcomposer.h> + +namespace android { +namespace intel { + +class HwcLayerList; + +class IDisplayContext { +public: + IDisplayContext() {} + virtual ~IDisplayContext() {} +public: + virtual bool initialize() = 0; + virtual void deinitialize() = 0; + virtual bool commitBegin(size_t numDisplays, hwc_display_contents_1_t **displays) = 0; + virtual bool commitContents(hwc_display_contents_1_t *display, HwcLayerList *layerList) = 0; + virtual bool commitEnd(size_t numDisplays, hwc_display_contents_1_t **displays) = 0; + virtual bool compositionComplete() = 0; + virtual bool setCursorPosition(int disp, int x, int y) = 0; +}; + +} +} + +#endif /* IDISPLAY_CONTEXT_H */ diff --git a/merrifield/include/IDisplayDevice.h b/merrifield/include/IDisplayDevice.h new file mode 100644 index 0000000..d9a6ac2 --- /dev/null +++ b/merrifield/include/IDisplayDevice.h @@ -0,0 +1,107 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef IDISPLAY_DEVICE_H +#define IDISPLAY_DEVICE_H + +#include <Dump.h> +#include <IDisplayContext.h> +#include <DisplayPlane.h> + +namespace android { +namespace intel { + +// display config +class DisplayConfig { +public: + DisplayConfig(int rr, int w, int h, int dpix, int dpiy) + : mRefreshRate(rr), + mWidth(w), + mHeight(h), + mDpiX(dpix), + mDpiY(dpiy) + {} +public: + int getRefreshRate() const { return mRefreshRate; } + int getWidth() const { return mWidth; } + int getHeight() const { return mHeight; } + int getDpiX() const { return mDpiX; } + int getDpiY() const { return mDpiY; } +private: + int mRefreshRate; + int mWidth; + int mHeight; + int mDpiX; + int mDpiY; +}; + + +// display device interface +class IDisplayDevice { +public: + // display device type + enum { + DEVICE_PRIMARY = HWC_DISPLAY_PRIMARY, + DEVICE_EXTERNAL = HWC_DISPLAY_EXTERNAL, +#ifdef INTEL_WIDI_MERRIFIELD + DEVICE_VIRTUAL = HWC_DISPLAY_VIRTUAL, +#endif + DEVICE_COUNT, + }; + enum { + DEVICE_DISCONNECTED = 0, + DEVICE_CONNECTED, + }; + enum { + DEVICE_DISPLAY_OFF = 0, + DEVICE_DISPLAY_ON, + DEVICE_DISPLAY_STANDBY, + }; +public: + IDisplayDevice() {} + virtual ~IDisplayDevice() {} +public: + virtual bool prePrepare(hwc_display_contents_1_t *display) = 0; + virtual bool prepare(hwc_display_contents_1_t *display) = 0; + virtual bool commit(hwc_display_contents_1_t *display, + IDisplayContext *context) = 0; + + virtual bool vsyncControl(bool enabled) = 0; + virtual bool blank(bool blank) = 0; + virtual bool getDisplaySize(int *width, int *height) = 0; + virtual bool getDisplayConfigs(uint32_t *configs, + size_t *numConfigs) = 0; + virtual bool getDisplayAttributes(uint32_t config, + const uint32_t *attributes, + int32_t *values) = 0; + virtual bool compositionComplete() = 0; + + virtual bool setPowerMode(int mode) = 0; + virtual int getActiveConfig() = 0; + virtual bool setActiveConfig(int index) = 0; + + virtual bool initialize() = 0; + virtual void deinitialize() = 0; + virtual bool isConnected() const = 0; + virtual const char* getName() const = 0; + virtual int getType() const = 0; + virtual void onVsync(int64_t timestamp) = 0; + virtual void dump(Dump& d) = 0; +}; + +} +} + +#endif /* IDISPLAY_DEVICE_H */ diff --git a/merrifield/include/IHdcpControl.h b/merrifield/include/IHdcpControl.h new file mode 100644 index 0000000..31a3bfd --- /dev/null +++ b/merrifield/include/IHdcpControl.h @@ -0,0 +1,38 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef IHDCP_CONTROL_H +#define IHDCP_CONTROL_H + +namespace android { +namespace intel { + +typedef void (*HdcpStatusCallback)(bool success, void *userData); + +class IHdcpControl { +public: + IHdcpControl() {} + virtual ~IHdcpControl() {} +public: + virtual bool startHdcp() = 0; + virtual bool startHdcpAsync(HdcpStatusCallback cb, void *userData) = 0; + virtual bool stopHdcp() = 0; +}; + +} // namespace intel +} // namespace android + + +#endif /* IHDCP_CONTROL_H */ diff --git a/merrifield/include/IPlatFactory.h b/merrifield/include/IPlatFactory.h new file mode 100644 index 0000000..71f3fe7 --- /dev/null +++ b/merrifield/include/IPlatFactory.h @@ -0,0 +1,45 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef IPLATFORM_FACTORY_H_ +#define IPLATFORM_FACTORY_H_ + + +#include <IDisplayDevice.h> +#include <BufferManager.h> +#include <IDisplayContext.h> +#include <DisplayPlaneManager.h> +#include <IVideoPayloadManager.h> + + +namespace android { +namespace intel { + + +class IPlatFactory { + +public: + virtual ~IPlatFactory() {}; +public: + virtual DisplayPlaneManager* createDisplayPlaneManager() = 0; + virtual BufferManager* createBufferManager() = 0; + virtual IDisplayDevice* createDisplayDevice(int disp) = 0; + virtual IDisplayContext* createDisplayContext() = 0; + virtual IVideoPayloadManager* createVideoPayloadManager() = 0; +}; +} // namespace intel +} // namespace android + +#endif /* DATABUFFER_H__ */ diff --git a/merrifield/include/IPrepareListener.h b/merrifield/include/IPrepareListener.h new file mode 100644 index 0000000..57dbba8 --- /dev/null +++ b/merrifield/include/IPrepareListener.h @@ -0,0 +1,33 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef IPREPARE_LISTENER_H +#define IPREPARE_LISTENER_H + +namespace android { +namespace intel { + +class IPrepareListener { +public: + IPrepareListener() {} + virtual ~IPrepareListener() {} +public: + virtual void onProtectedLayerStart(int disp) = 0; +}; + +} // namespace intel +} // namespace android + +#endif /* IPREPARE_LISTENER_H */ diff --git a/merrifield/include/IVideoPayloadManager.h b/merrifield/include/IVideoPayloadManager.h new file mode 100644 index 0000000..3aa0fc6 --- /dev/null +++ b/merrifield/include/IVideoPayloadManager.h @@ -0,0 +1,62 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef IVIDEO_PAYLOAD_MANAGER_H +#define IVIDEO_PAYLOAD_MANAGER_H + +#include <hardware/hwcomposer.h> + +namespace android { +namespace intel { + +class BufferMapper; + +class IVideoPayloadManager { +public: + IVideoPayloadManager() {} + virtual ~IVideoPayloadManager() {} + +public: + struct Buffer { + buffer_handle_t khandle; + uint16_t width; + uint16_t height; + uint16_t bufWidth; + uint16_t bufHeight; + uint16_t lumaStride; + uint16_t chromaUStride; + uint16_t chromaVStride; + uint16_t offsetX; + uint16_t offsetY; + bool tiled; + }; + struct MetaData { + uint32_t format; + uint32_t transform; + int64_t timestamp; + Buffer normalBuffer; + Buffer scalingBuffer; + Buffer rotationBuffer; + }; + +public: + virtual bool getMetaData(BufferMapper *mapper, MetaData *metadata) = 0; + virtual bool setRenderStatus(BufferMapper *mapper, bool renderStatus) = 0; +}; + +} // namespace intel +} // namespace android + +#endif /* IVIDEO_PAYLOAD_MANAGER_H */ diff --git a/merrifield/include/IVsyncControl.h b/merrifield/include/IVsyncControl.h new file mode 100644 index 0000000..5edc77b --- /dev/null +++ b/merrifield/include/IVsyncControl.h @@ -0,0 +1,37 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef IVSYNCCONTROL_H_ +#define IVSYNCCONTROL_H_ + +namespace android { +namespace intel { + +class IVsyncControl { +public: + IVsyncControl() {}; + virtual ~IVsyncControl() {}; +public: + virtual bool initialize() = 0; + virtual void deinitialize() = 0; + virtual bool control(int disp, bool enabled) = 0; + virtual bool wait(int disp, int64_t& timestamp) = 0; +}; + +} // namespace intel +} // namespace android + + +#endif /* IVSYNCCONTROL_H_ */ diff --git a/merrifield/include/PhysicalDevice.h b/merrifield/include/PhysicalDevice.h new file mode 100644 index 0000000..9bbb90d --- /dev/null +++ b/merrifield/include/PhysicalDevice.h @@ -0,0 +1,125 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef PHYSICAL_DEVICE_H +#define PHYSICAL_DEVICE_H + +#include <DisplayPlane.h> +#include <IVsyncControl.h> +#include <IBlankControl.h> +#include <IPrepareListener.h> +#include <VsyncEventObserver.h> +#include <HwcLayerList.h> +#include <Drm.h> +#include <IDisplayDevice.h> + +namespace android { +namespace intel { + +class IHdcpControl; + +class DeviceControlFactory { +public: + virtual ~DeviceControlFactory(){} +public: + virtual IVsyncControl* createVsyncControl() = 0; + virtual IBlankControl* createBlankControl() = 0; + virtual IHdcpControl* createHdcpControl() = 0; +}; + +class Hwcomposer; + +// Base class for primary and external devices +class PhysicalDevice : public IDisplayDevice { +public: + PhysicalDevice(uint32_t type, Hwcomposer& hwc, DeviceControlFactory* controlFactory); + virtual ~PhysicalDevice(); +public: + virtual bool prePrepare(hwc_display_contents_1_t *display); + virtual bool prepare(hwc_display_contents_1_t *display); + virtual bool commit(hwc_display_contents_1_t *display, IDisplayContext *context); + + virtual bool vsyncControl(bool enabled); + virtual bool blank(bool blank); + virtual bool getDisplaySize(int *width, int *height); + virtual bool getDisplayConfigs(uint32_t *configs, + size_t *numConfigs); + virtual bool getDisplayAttributes(uint32_t config, + const uint32_t *attributes, + int32_t *values); + virtual bool compositionComplete(); + + virtual bool setPowerMode(int mode); + virtual int getActiveConfig(); + virtual bool setActiveConfig(int index); + + // display config operations + virtual void removeDisplayConfigs(); + virtual bool detectDisplayConfigs(); + + // device related operations + virtual bool initCheck() const { return mInitialized; } + virtual bool initialize(); + virtual void deinitialize(); + virtual bool isConnected() const; + virtual const char* getName() const; + virtual int getType() const; + + //events + virtual void onVsync(int64_t timestamp); + + virtual void dump(Dump& d); + +protected: + void onGeometryChanged(hwc_display_contents_1_t *list); + bool updateDisplayConfigs(); + IVsyncControl* createVsyncControl() {return mControlFactory->createVsyncControl();} + friend class VsyncEventObserver; + +protected: + uint32_t mType; + const char *mName; + + Hwcomposer& mHwc; + + // display configs + Vector<DisplayConfig*> mDisplayConfigs; + int mActiveDisplayConfig; + + + IBlankControl *mBlankControl; + VsyncEventObserver *mVsyncObserver; + + DeviceControlFactory *mControlFactory; + + // layer list + HwcLayerList *mLayerList; + bool mConnected; + bool mBlank; + + // lock + Mutex mLock; + + // DPMS on (1) or off (0) + int mDisplayState; + bool mInitialized; +}; + + + +} +} + +#endif /* PHYSICAL_DEVICE_H */ diff --git a/merrifield/include/PlaneCapabilities.h b/merrifield/include/PlaneCapabilities.h new file mode 100644 index 0000000..0962fda --- /dev/null +++ b/merrifield/include/PlaneCapabilities.h @@ -0,0 +1,38 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef PLANE_CAPABILITIES_H +#define PLANE_CAPABILITIES_H + +#include <DataBuffer.h> + +namespace android { +namespace intel { + +class HwcLayer; +class PlaneCapabilities +{ +public: + static bool isFormatSupported(int planeType, HwcLayer *hwcLayer); + static bool isSizeSupported(int planeType, HwcLayer *hwcLayer); + static bool isBlendingSupported(int planeType, HwcLayer *hwcLayer); + static bool isScalingSupported(int planeType, HwcLayer *hwcLayer); + static bool isTransformSupported(int planeType, HwcLayer *hwcLayer); +}; + +} // namespace intel +} // namespace android + +#endif /*PLANE_CAPABILITIES_H*/ diff --git a/merrifield/include/PrimaryDevice.h b/merrifield/include/PrimaryDevice.h new file mode 100644 index 0000000..6354193 --- /dev/null +++ b/merrifield/include/PrimaryDevice.h @@ -0,0 +1,47 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef PRIMARY_DEVICE_H +#define PRIMARY_DEVICE_H + +#include <DisplayPlane.h> +#include <IVsyncControl.h> +#include <IBlankControl.h> +#include <VsyncEventObserver.h> +#include <HwcLayerList.h> +#include <PhysicalDevice.h> + +namespace android { +namespace intel { + + +class PrimaryDevice : public PhysicalDevice { +public: + PrimaryDevice(Hwcomposer& hwc, DeviceControlFactory* controlFactory); + virtual ~PrimaryDevice(); +public: + virtual bool initialize(); + virtual void deinitialize(); + + bool blank(bool blank); +private: + static void repeatedFrameEventListener(void *data); + void repeatedFrameListener(); +}; + +} +} + +#endif /* PRIMARY_DEVICE_H */ diff --git a/merrifield/include/UeventObserver.h b/merrifield/include/UeventObserver.h new file mode 100755 index 0000000..b1ca781 --- /dev/null +++ b/merrifield/include/UeventObserver.h @@ -0,0 +1,64 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef UEVENT_OBSERVER_H +#define UEVENT_OBSERVER_H + +#include <utils/KeyedVector.h> +#include <utils/String8.h> +#include <SimpleThread.h> + +namespace android { +namespace intel { + +typedef void (*UeventListenerFunc)(void *data); + +class UeventObserver +{ +public: + UeventObserver(); + virtual ~UeventObserver(); + +public: + bool initialize(); + void deinitialize(); + void start(); + void registerListener(const char *event, UeventListenerFunc func, void *data); + +private: + DECLARE_THREAD(UeventObserverThread, UeventObserver); + void onUevent(); + +private: + enum { + UEVENT_MSG_LEN = 4096, + }; + + char mUeventMessage[UEVENT_MSG_LEN]; + int mUeventFd; + int mExitRDFd; + int mExitWDFd; + struct UeventListener { + UeventListenerFunc func; + void *data; + }; + KeyedVector<String8, UeventListener*> mListeners; +}; + +} // namespace intel +} // namespace android + +#endif + diff --git a/merrifield/include/VirtualDevice.h b/merrifield/include/VirtualDevice.h new file mode 100755 index 0000000..2af3c6b --- /dev/null +++ b/merrifield/include/VirtualDevice.h @@ -0,0 +1,221 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef VIRTUAL_DEVICE_H +#define VIRTUAL_DEVICE_H + +#include <IDisplayDevice.h> +#include <SimpleThread.h> +#include <IVideoPayloadManager.h> +#include <utils/Condition.h> +#include <utils/Mutex.h> +#include <utils/Vector.h> + +#include "IFrameServer.h" + +#include <va/va.h> +#include <va/va_vpp.h> + +namespace android { +namespace intel { + +class Hwcomposer; +class DisplayPlaneManager; +class IVideoPayloadManager; +class SoftVsyncObserver; + +class VirtualDevice : public IDisplayDevice, public BnFrameServer { +protected: + class VAMappedHandle; + class VAMappedHandleObject; + struct CachedBuffer : public android::RefBase { + CachedBuffer(BufferManager *mgr, buffer_handle_t handle); + ~CachedBuffer(); + BufferManager *manager; + BufferMapper *mapper; + VAMappedHandle *vaMappedHandle; + buffer_handle_t cachedKhandle; + }; + struct HeldDecoderBuffer : public android::RefBase { + HeldDecoderBuffer(const sp<VirtualDevice>& vd, const android::sp<CachedBuffer>& cachedBuffer); + virtual ~HeldDecoderBuffer(); + android::sp<VirtualDevice> vd; + android::sp<CachedBuffer> cachedBuffer; + }; + struct Configuration { + sp<IFrameTypeChangeListener> typeChangeListener; + sp<IFrameListener> frameListener; + FrameProcessingPolicy policy; + bool frameServerActive; + bool extendedModeEnabled; + bool forceNotifyFrameType; + bool forceNotifyBufferInfo; + }; + class BufferList { + public: + BufferList(VirtualDevice& vd, const char* name, uint32_t limit, uint32_t format, uint32_t usage); + buffer_handle_t get(uint32_t width, uint32_t height, sp<RefBase>* heldBuffer); + void clear(); + private: + struct HeldBuffer; + VirtualDevice& mVd; + const char* mName; + android::List<buffer_handle_t> mAvailableBuffers; + const uint32_t mLimit; + const uint32_t mFormat; + const uint32_t mUsage; + uint32_t mBuffersToCreate; + uint32_t mWidth; + uint32_t mHeight; + }; + struct Task; + struct RenderTask; + struct ComposeTask; + struct EnableVspTask; + struct DisableVspTask; + struct BlitTask; + struct FrameTypeChangedTask; + struct BufferInfoChangedTask; + struct OnFrameReadyTask; + + Mutex mConfigLock; + Configuration mCurrentConfig; + Configuration mNextConfig; + ssize_t mRgbLayer; + ssize_t mYuvLayer; + bool mProtectedMode; + + buffer_handle_t mExtLastKhandle; + int64_t mExtLastTimestamp; + + int64_t mRenderTimestamp; + + Mutex mTaskLock; // for task queue and buffer lists + BufferList mCscBuffers; + BufferList mRgbUpscaleBuffers; + DECLARE_THREAD(WidiBlitThread, VirtualDevice); + Condition mRequestQueued; + Condition mRequestDequeued; + Vector< sp<Task> > mTasks; + + // fence info + int mSyncTimelineFd; + unsigned mNextSyncPoint; + bool mExpectAcquireFences; + + FrameInfo mLastInputFrameInfo; + FrameInfo mLastOutputFrameInfo; + + int32_t mVideoFramerate; + + android::KeyedVector<buffer_handle_t, android::sp<CachedBuffer> > mMappedBufferCache; + android::Mutex mHeldBuffersLock; + android::KeyedVector<buffer_handle_t, android::sp<android::RefBase> > mHeldBuffers; + + // VSP + bool mVspInUse; + bool mVspEnabled; + uint32_t mVspWidth; + uint32_t mVspHeight; + VADisplay va_dpy; + VAConfigID va_config; + VAContextID va_context; + VASurfaceID va_blank_yuv_in; + VASurfaceID va_blank_rgb_in; + android::KeyedVector<buffer_handle_t, android::sp<VAMappedHandleObject> > mVaMapCache; + + bool mVspUpscale; + bool mDebugVspClear; + bool mDebugVspDump; + uint32_t mDebugCounter; + +private: + android::sp<CachedBuffer> getMappedBuffer(buffer_handle_t handle); + + bool sendToWidi(hwc_display_contents_1_t *display); + bool queueCompose(hwc_display_contents_1_t *display); + bool queueColorConvert(hwc_display_contents_1_t *display); + bool handleExtendedMode(hwc_display_contents_1_t *display); + + void queueFrameTypeInfo(const FrameInfo& inputFrameInfo); + void queueBufferInfo(const FrameInfo& outputFrameInfo); + + void colorSwap(buffer_handle_t src, buffer_handle_t dest, uint32_t pixelCount); + void vspPrepare(uint32_t width, uint32_t height); + void vspEnable(uint32_t width, uint32_t height); + void vspDisable(); + void vspCompose(VASurfaceID videoIn, VASurfaceID rgbIn, VASurfaceID videoOut, + const VARectangle* surface_region, const VARectangle* output_region); + + bool getFrameOfSize(uint32_t width, uint32_t height, const IVideoPayloadManager::MetaData& metadata, IVideoPayloadManager::Buffer& info); + void setMaxDecodeResolution(uint32_t width, uint32_t height); + +public: + VirtualDevice(Hwcomposer& hwc); + virtual ~VirtualDevice(); + bool isFrameServerActive() const; + +public: + virtual bool prePrepare(hwc_display_contents_1_t *display); + virtual bool prepare(hwc_display_contents_1_t *display); + virtual bool commit(hwc_display_contents_1_t *display, + IDisplayContext *context); + + virtual bool vsyncControl(bool enabled); + virtual bool blank(bool blank); + virtual bool getDisplaySize(int *width, int *height); + virtual bool getDisplayConfigs(uint32_t *configs, + size_t *numConfigs); + virtual bool getDisplayAttributes(uint32_t config, + const uint32_t *attributes, + int32_t *values); + virtual bool compositionComplete(); + virtual bool initialize(); + virtual void deinitialize(); + virtual bool isConnected() const; + virtual const char* getName() const; + virtual int getType() const; + virtual void onVsync(int64_t timestamp); + virtual void dump(Dump& d); + + // IFrameServer methods + virtual android::status_t start(sp<IFrameTypeChangeListener> frameTypeChangeListener); + virtual android::status_t stop(bool isConnected); + /* TODO: 64-bit - this handle of size 32-bit is a problem for 64-bit */ + virtual android::status_t notifyBufferReturned(int handle); + virtual android::status_t setResolution(const FrameProcessingPolicy& policy, android::sp<IFrameListener> listener); + virtual bool setPowerMode(int mode); + virtual int getActiveConfig(); + virtual bool setActiveConfig(int index); + +protected: + bool mInitialized; + Hwcomposer& mHwc; + IVideoPayloadManager *mPayloadManager; + SoftVsyncObserver *mVsyncObserver; + uint32_t mOrigContentWidth; + uint32_t mOrigContentHeight; + bool mFirstVideoFrame; + bool mLastConnectionStatus; + uint32_t mCachedBufferCapcity; + uint32_t mDecWidth; + uint32_t mDecHeight; + bool mIsForceCloneMode; +}; + +} +} + +#endif /* VIRTUAL_DEVICE_H */ diff --git a/merrifield/include/pvr/hal/hal_public.h b/merrifield/include/pvr/hal/hal_public.h new file mode 100644 index 0000000..9cd6db2 --- /dev/null +++ b/merrifield/include/pvr/hal/hal_public.h @@ -0,0 +1,257 @@ +/* Copyright (c) Imagination Technologies Ltd. + * + * The contents of this file are subject to the MIT license as set out below. + * + * 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 + * AUTHORS OR COPYRIGHT HOLDERS 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 HAL_PUBLIC_H +#define HAL_PUBLIC_H + +/* Authors of third party hardware composer (HWC) modules will need to include + * this header to access functionality in the gralloc HAL. + */ + +#define PVR_ANDROID_NATIVE_WINDOW_HAS_SYNC + +#include <hardware/gralloc.h> +#include <hardware/hwcomposer.h> + +#define ALIGN(x,a) (((x) + (a) - 1L) & ~((a) - 1L)) +#define HW_ALIGN 32 +#define CAMERA_ALIGN 64 + +/** YV12 specific (to handle different alignment) ****************************/ + +/* We must align YV12 to a multiple of 32bytes as NEON optimizations + * in stagefright require the YV12 planes to be 128bit aligned. + * while display controller requires 64 bytes alignement + */ +#define YV12_ALIGN 128 + +#define HAL_PIXEL_FORMAT_BGRX_8888 0x101 // Keep consistent with android_utils.h +enum { + HAL_PIXEL_FORMAT_NV12 = 0x3231564E, // YCrCb 4:2:0 SP + HAL_PIXEL_FORMAT_NV21 = 0x3132564E, // YCrCb 4:2:0 SP + HAL_PIXEL_FORMAT_I420 = 0x30323449, + HAL_PIXEL_FORMAT_YUY2 = 0x32595559, + HAL_PIXEL_FORMAT_UYVY = 0x59565955, + + // Intel video decode formats + HAL_PIXEL_FORMAT_NV12_VED = 0x7FA00E00, //OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar + HAL_PIXEL_FORMAT_NV12_VEDT = 0x7FA00F00, //OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar_Tiled + + HAL_PIXEL_FORMAT_YCbCr_422_P = 0x12, // IYUV + HAL_PIXEL_FORMAT_YCbCr_420_P = 0x13, // YUV9 + HAL_PIXEL_FORMAT_YCbCr_420_I = 0x15, + + HAL_PIXEL_FORMAT_INTEL_UYVY = 0x107, + HAL_PIXEL_FORMAT_YCbCr_420_SP = 0x108, + HAL_PIXEL_FORMAT_ZSL = 0x109, +}; + +/* This can be tuned down as appropriate for the SOC. + * + * IMG formats are usually a single sub-alloc. + * Some OEM video formats are two sub-allocs (Y, UV planes). + * Future OEM video formats might be three sub-allocs (Y, U, V planes). + */ +#define MAX_SUB_ALLOCS 3 + + +/* This defines the maximum server sync objects used per allocation. */ + +/* Note: It's unfortunate that we have to change the handle size dependent + * on a build option, but we have no choice because 'fd' fields must all + * be utilized so they are valid to be dup'ed, and we don't need some of + * the extra fds in a native_fence_sync build. + */ +#if defined(PVR_ANDROID_NATIVE_WINDOW_HAS_SYNC) +#define MAX_SRV_SYNC_OBJS 2 +#else +#define MAX_SRV_SYNC_OBJS 4 +#endif + +typedef struct +{ + native_handle_t base; + + /* These fields can be sent cross process. They are also valid + * to duplicate within the same process. + * + * A table is stored within psPrivateData on gralloc_module_t (this + * is obviously per-process) which maps stamps to a mapped + * PVRSRV_MEMDESC in that process. Each map entry has a lock + * count associated with it, satisfying the requirements of the + * Android API. This also prevents us from leaking maps/allocations. + * + * This table has entries inserted either by alloc() + * (alloc_device_t) or map() (gralloc_module_t). Entries are removed + * by free() (alloc_device_t) and unmap() (gralloc_module_t). + */ + +#define IMG_NATIVE_HANDLE_NUMFDS (MAX_SRV_SYNC_OBJS + MAX_SUB_ALLOCS) + /* The `syncfd' field is used to export PVRSRV_CLIENT_SYNC_PRIM to + * another process. Its producer/consumer rules should match the + * PVRSRV_MEMDESC handles, except that there is only one sync + * per N memdesc objects. + * + * This should be listed before `fd' because it is not variable + * width. The problem with variable width is that in the case we + * export framebuffer allocations, we may want to patch some of + * the fds to (unused) ints, so we can't leave gaps. + */ + int aiSyncFD[MAX_SRV_SYNC_OBJS]; + + /* The `fd' field is used to "export" a meminfo to another process. + * Therefore, it is allocated by alloc_device_t, and consumed by + * gralloc_module_t. + */ + int fd[MAX_SUB_ALLOCS]; + +#define IMG_NATIVE_HANDLE_NUMINTS ((sizeof(unsigned long long) / sizeof(int)) + 5) + /* A KERNEL unique identifier for any exported kernel meminfo. Each + * exported kernel meminfo will have a unique stamp, but note that in + * userspace, several meminfos across multiple processes could have + * the same stamp. As the native_handle can be dup(2)'d, there could be + * multiple handles with the same stamp but different file descriptors. + */ + unsigned long long ui64Stamp; + + /* This is used for buffer usage validation when locking a buffer, + * and also in WSEGL (for the composition bypass feature). + */ + int usage; + + /* In order to do efficient cache flushes we need the buffer dimensions + * and format. These are available on the ANativeWindowBuffer, + * but the platform doesn't pass them down to the graphics HAL. + * + * These fields are also used in the composition bypass. In this + * capacity, these are the "real" values for the backing allocation. + */ + int iWidth; + int iHeight; + int iFormat; + unsigned int uiBpp; +} +__attribute__((aligned(sizeof(int)),packed)) IMG_native_handle_t; + +typedef struct +{ + int l, t, w, h; +} +IMG_write_lock_rect_t; + +/* Keep this in sync with SGX */ +typedef int (*IMG_buffer_format_compute_params_pfn)( + unsigned int uiPlane, int *piWidth, int *piHeight, int *piStride, + int *piVStride, unsigned long *pulPlaneOffset); + +#define IMG_BFF_YUV (1 << 0) +#define IMG_BFF_UVCbCrORDERING (1 << 1) +#define IMG_BFF_CPU_CLEAR (1 << 2) +#define IMG_BFF_DONT_GPU_CLEAR (1 << 3) +#define IMG_BFF_PARTIAL_ALLOC (1 << 4) +#define IMG_BFF_NEVER_COMPRESS (1 << 5) + +/* Keep this in sync with SGX */ +typedef struct IMG_buffer_format_public_t +{ + /* Buffer formats are returned as a linked list */ + struct IMG_buffer_format_public_t *psNext; + + /* HAL_PIXEL_FORMAT_... enumerant */ + int iHalPixelFormat; + + /* IMG_PIXFMT_... enumerant */ + int iIMGPixelFormat; + + /* Friendly name for format */ + const char *const szName; + + /* Bits (not bytes) per pixel */ + unsigned int uiBpp; + + /* Supported HW usage bits. If this is GRALLOC_USAGE_HW_MASK, all usages + * are supported. Used for HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED. + */ + int iSupportedUsage; + + /* Allocation description flags */ + unsigned int uiFlags; + + /* Utility function for adjusting YUV per-plane parameters */ + IMG_buffer_format_compute_params_pfn pfnComputeParams; +} +IMG_buffer_format_public_t; + +typedef struct +{ + /* The original hwc layer */ + hwc_layer_1_t *psLayer; + + /* Custom data for the display engine */ + unsigned long custom; +} +IMG_hwc_layer_t; + +typedef struct IMG_display_device_public_t { + int (*post)(struct IMG_display_device_public_t *dev, IMG_hwc_layer_t *layers, + int num_layers, int *releaseFenceFd); +} IMG_display_device_public_t; + +typedef struct IMG_gralloc_module_public_t +{ + gralloc_module_t base; + IMG_display_device_public_t *psDisplayDevice; + + /* Gets the head of the linked list of all registered formats */ + const IMG_buffer_format_public_t *(*GetBufferFormats)(void); + + /* Functionality before this point should be in sync with SGX. + * After this point will be different. + */ + + /* Custom-blit components in lieu of overlay hardware */ + int (*Blit)(struct IMG_gralloc_module_public_t const *module, + buffer_handle_t src, buffer_handle_t dest, + int w, int h, int x, int y, + int filter, + int transform, + int async); + + int (*Blit3)(struct IMG_gralloc_module_public_t const *module, + unsigned long long ui64SrcStamp, int iSrcWidth, + int iSrcHeight, int iSrcFormat, int eSrcRotation, + buffer_handle_t dest, int eDestRotation); + + /* Walk the above list and return only the specified format */ + const IMG_buffer_format_public_t *(*GetBufferFormat)(int iFormat); +/* intel hwc extension */ + int (*getCpuAddress)(struct IMG_gralloc_module_public_t const *module, + buffer_handle_t handle, + void **virt, uint32_t *size); + int (*putCpuAddress)(struct IMG_gralloc_module_public_t const *module, + buffer_handle_t handle); + IMG_display_device_public_t *(*getDisplayDevice)(struct IMG_gralloc_module_public_t *module); +} +IMG_gralloc_module_public_t; + +#endif /* HAL_PUBLIC_H */ diff --git a/merrifield/ips/anniedale/AnnCursorPlane.cpp b/merrifield/ips/anniedale/AnnCursorPlane.cpp new file mode 100644 index 0000000..c6607dd --- /dev/null +++ b/merrifield/ips/anniedale/AnnCursorPlane.cpp @@ -0,0 +1,223 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <HwcTrace.h> +#include <Hwcomposer.h> +#include <BufferManager.h> +#include <anniedale/AnnCursorPlane.h> +#include <tangier/TngGrallocBuffer.h> +#include <hal_public.h> + +namespace android { +namespace intel { + +AnnCursorPlane::AnnCursorPlane(int index, int disp) + : DisplayPlane(index, PLANE_CURSOR, disp) +{ + CTRACE(); + memset(&mContext, 0, sizeof(mContext)); + memset(&mCrop, 0, sizeof(mCrop)); +} + +AnnCursorPlane::~AnnCursorPlane() +{ + CTRACE(); +} + +bool AnnCursorPlane::enable() +{ + return enablePlane(true); + +} + +bool AnnCursorPlane::disable() +{ + return enablePlane(false); +} + +bool AnnCursorPlane::reset() +{ + // clear mCrop once reset + memset(&mCrop, 0, sizeof(mCrop)); + return true; +} + +void* AnnCursorPlane::getContext() const +{ + CTRACE(); + return (void *)&mContext; +} + +void AnnCursorPlane::setZOrderConfig(ZOrderConfig& config, void *nativeConfig) +{ + (void) config; + (void) nativeConfig; + + CTRACE(); +} + +bool AnnCursorPlane::setDataBuffer(buffer_handle_t handle) +{ + bool ret; + + if (!handle) { + ETRACE("handle is NULL"); + return false; + } + + ret = DisplayPlane::setDataBuffer(handle); + if (ret == false) { + ETRACE("failed to set data buffer"); + return ret; + } + + return true; +} + +bool AnnCursorPlane::setDataBuffer(BufferMapper& mapper) +{ + int w = mapper.getWidth(); + int h = mapper.getHeight(); + int cursorSize = 0; + + CTRACE(); + + // setup plane position + int dstX = mPosition.x; + int dstY = mPosition.y; + + if (h < w) { + cursorSize = h; + } else { + cursorSize = w; + } + + uint32_t cntr = 0; + if (64 <= cursorSize && cursorSize < 128) { + cursorSize = 64; + cntr = 0x7; + } else if (128 <= cursorSize && cursorSize < 256) { + cursorSize = 128; + cntr = 0x2; + } else { + cursorSize = 256; + cntr = 0x3; + } + + if (mapper.getFormat() == HAL_PIXEL_FORMAT_RGBA_8888) { + cntr |= 1 << 5; + } else if (mapper.getFormat() == HAL_PIXEL_FORMAT_BGRA_8888) { + // swap color from BGRA to RGBA - alpha is MSB + uint8_t *p = (uint8_t *)(mapper.getCpuAddress(0)); + uint8_t *srcPixel; + uint32_t stride = mapper.getStride().rgb.stride; + uint8_t temp; + if (!p) { + return false; + } + + for (int i = 0; i < cursorSize; i++) { + for (int j = 0; j < cursorSize; j++) { + srcPixel = p + i*stride + j*4; + temp = srcPixel[0]; + srcPixel[0] = srcPixel[2]; + srcPixel[2] = temp; + } + } + cntr |= 1 << 5; + } else { + ETRACE("invalid color format"); + return false; + } + + // update context + mContext.type = DC_CURSOR_PLANE; + mContext.ctx.cs_ctx.index = mIndex; + mContext.ctx.cs_ctx.pipe = mDevice; + mContext.ctx.cs_ctx.cntr = cntr; + mContext.ctx.cs_ctx.surf = mapper.getGttOffsetInPage(0) << 12; + + mContext.ctx.cs_ctx.pos = 0; + if (dstX < 0) { + mContext.ctx.cs_ctx.pos |= 1 << 15; + dstX = -dstX; + } + if (dstY < 0) { + mContext.ctx.cs_ctx.pos |= 1 << 31; + dstY = -dstY; + } + mContext.ctx.cs_ctx.pos |= (dstY & 0xfff) << 16 | (dstX & 0xfff); + return true; +} + +bool AnnCursorPlane::enablePlane(bool enabled) +{ + RETURN_FALSE_IF_NOT_INIT(); + + struct drm_psb_register_rw_arg arg; + memset(&arg, 0, sizeof(struct drm_psb_register_rw_arg)); + if (enabled) { + arg.plane_enable_mask = 1; + } else { + arg.plane_disable_mask = 1; + } + + arg.plane.type = DC_CURSOR_PLANE; + arg.plane.index = mIndex; + arg.plane.ctx = 0; + + // issue ioctl + Drm *drm = Hwcomposer::getInstance().getDrm(); + bool ret = drm->writeReadIoctl(DRM_PSB_REGISTER_RW, &arg, sizeof(arg)); + if (ret == false) { + WTRACE("plane enabling (%d) failed with error code %d", enabled, ret); + return false; + } + + return true; +} + +bool AnnCursorPlane::isDisabled() +{ + RETURN_FALSE_IF_NOT_INIT(); + + struct drm_psb_register_rw_arg arg; + memset(&arg, 0, sizeof(struct drm_psb_register_rw_arg)); + + arg.plane.type = DC_CURSOR_PLANE; + arg.get_plane_state_mask = 1; + arg.plane.index = mIndex; + arg.plane.ctx = 0; + + // issue ioctl + Drm *drm = Hwcomposer::getInstance().getDrm(); + bool ret = drm->writeReadIoctl(DRM_PSB_REGISTER_RW, &arg, sizeof(arg)); + if (ret == false) { + WTRACE("plane state query failed with error code %d", ret); + return false; + } + + return arg.plane.ctx == PSB_DC_PLANE_DISABLED; +} + +void AnnCursorPlane::postFlip() +{ + // prevent mUpdateMasks from being reset + // skipping flip may cause flicking +} + +} // namespace intel +} // namespace android diff --git a/merrifield/ips/anniedale/AnnCursorPlane.h b/merrifield/ips/anniedale/AnnCursorPlane.h new file mode 100644 index 0000000..88d8075 --- /dev/null +++ b/merrifield/ips/anniedale/AnnCursorPlane.h @@ -0,0 +1,59 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef ANN_CUR_PLANE_H +#define ANN_CUR_PLANE_H + +#include <utils/KeyedVector.h> +#include <hal_public.h> +#include <Hwcomposer.h> +#include <BufferCache.h> +#include <DisplayPlane.h> + +#include <linux/psb_drm.h> + +namespace android { +namespace intel { + +class AnnCursorPlane : public DisplayPlane { +public: + AnnCursorPlane(int index, int disp); + virtual ~AnnCursorPlane(); +public: + // hardware operations + bool enable(); + bool disable(); + bool reset(); + bool isDisabled(); + void postFlip(); + + void* getContext() const; + void setZOrderConfig(ZOrderConfig& config, void *nativeConfig); + + bool setDataBuffer(buffer_handle_t handle); +protected: + bool setDataBuffer(BufferMapper& mapper); + bool enablePlane(bool enabled); + +protected: + struct intel_dc_plane_ctx mContext; + crop_t mCrop; +}; + +} // namespace intel +} // namespace android + +#endif /* ANN_CUR_PLANE_H */ diff --git a/merrifield/ips/anniedale/AnnOverlayPlane.cpp b/merrifield/ips/anniedale/AnnOverlayPlane.cpp new file mode 100644 index 0000000..930f895 --- /dev/null +++ b/merrifield/ips/anniedale/AnnOverlayPlane.cpp @@ -0,0 +1,834 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <math.h> +#include <HwcTrace.h> +#include <Drm.h> +#include <Hwcomposer.h> +#include <anniedale/AnnOverlayPlane.h> +#include <tangier/TngGrallocBuffer.h> + +// FIXME: remove it +#include <OMX_IVCommon.h> +#include <OMX_IntelVideoExt.h> + +namespace android { +namespace intel { + +AnnOverlayPlane::AnnOverlayPlane(int index, int disp) + : OverlayPlaneBase(index, disp), + mRotationBufProvider(NULL), + mRotationConfig(0), + mZOrderConfig(0), + mUseOverlayRotation(true) +{ + CTRACE(); + + memset(&mContext, 0, sizeof(mContext)); +} + +AnnOverlayPlane::~AnnOverlayPlane() +{ + CTRACE(); +} + +void AnnOverlayPlane::setZOrderConfig(ZOrderConfig& zorderConfig, + void *nativeConfig) +{ + long slot = (long)nativeConfig; + + CTRACE(); + + switch (slot) { + case 0: + mZOrderConfig = 0; + break; + case 1: + mZOrderConfig = (1 << 8); + break; + case 2: + mZOrderConfig = (2 << 8); + break; + case 3: + mZOrderConfig = (3 << 8); + break; + default: + ETRACE("Invalid overlay plane zorder %ld", slot); + return; + } +} + +bool AnnOverlayPlane::reset() +{ + OverlayPlaneBase::reset(); + if (mRotationBufProvider) { + mRotationBufProvider->reset(); + } + return true; +} + +bool AnnOverlayPlane::enable() +{ + RETURN_FALSE_IF_NOT_INIT(); + + // by default always use overlay rotation + mUseOverlayRotation = true; + + if (mContext.ctx.ov_ctx.ovadd & (0x1 << 15)) + return true; + + mContext.ctx.ov_ctx.ovadd |= (0x1 << 15); + + // flush + flush(PLANE_ENABLE); + + return true; +} + +bool AnnOverlayPlane::disable() +{ + RETURN_FALSE_IF_NOT_INIT(); + + if (!(mContext.ctx.ov_ctx.ovadd & (0x1 << 15))) + return true; + + mContext.ctx.ov_ctx.ovadd &= ~(0x1 << 15); + + mContext.ctx.ov_ctx.ovadd &= ~(0x300); + + mContext.ctx.ov_ctx.ovadd |= mPipeConfig; + + // flush + flush(PLANE_DISABLE); + + return true; +} + +void AnnOverlayPlane::postFlip() +{ + // when using AnnOverlayPlane through AnnDisplayPlane as proxy, postFlip is never + // called so mUpdateMasks is never reset. + // When using AnnOverlayPlane directly, postFlip is invoked and mUpdateMasks is reset + // post-flip. + + // need to check why mUpdateMasks = 0 causes video freeze. + + //DisplayPlane::postFlip(); +} + + +void AnnOverlayPlane::resetBackBuffer(int buf) +{ + CTRACE(); + + if (!mBackBuffer[buf] || !mBackBuffer[buf]->buf) + return; + + OverlayBackBufferBlk *backBuffer = mBackBuffer[buf]->buf; + + memset(backBuffer, 0, sizeof(OverlayBackBufferBlk)); + + // reset overlay + backBuffer->OCLRC0 = (OVERLAY_INIT_CONTRAST << 18) | + (OVERLAY_INIT_BRIGHTNESS & 0xff); + backBuffer->OCLRC1 = OVERLAY_INIT_SATURATION; + backBuffer->DCLRKV = OVERLAY_INIT_COLORKEY; + backBuffer->DCLRKM = OVERLAY_INIT_COLORKEYMASK; + backBuffer->OCONFIG = 0; + backBuffer->OCONFIG |= (0x1 << 27); + // use 3 line buffers + backBuffer->OCONFIG |= 0x1; + backBuffer->SCHRKEN &= ~(0x7 << 24); + backBuffer->SCHRKEN |= 0xff; +} + +bool AnnOverlayPlane::bufferOffsetSetup(BufferMapper& mapper) +{ + CTRACE(); + + OverlayBackBufferBlk *backBuffer = mBackBuffer[mCurrent]->buf; + if (!backBuffer) { + ETRACE("invalid back buffer"); + return false; + } + + uint32_t format = mapper.getFormat(); + uint32_t gttOffsetInBytes = (mapper.getGttOffsetInPage(0) << 12); + uint32_t yStride = mapper.getStride().yuv.yStride; + uint32_t uvStride = mapper.getStride().yuv.uvStride; + uint32_t w = mapper.getWidth(); + uint32_t h = mapper.getHeight(); + uint32_t srcX= mapper.getCrop().x; + uint32_t srcY= mapper.getCrop().y; + uint32_t ySurface, uSurface, vSurface; + uint32_t yTileOffsetX, yTileOffsetY; + uint32_t uTileOffsetX, uTileOffsetY; + uint32_t vTileOffsetX, vTileOffsetY; + + // clear original format setting + backBuffer->OCMD &= ~(0xf << 10); + backBuffer->OCMD &= ~OVERLAY_MEMORY_LAYOUT_TILED; + + backBuffer->OBUF_0Y = 0; + backBuffer->OBUF_0V = 0; + backBuffer->OBUF_0U = 0; + // Y/U/V plane must be 4k bytes aligned. + ySurface = gttOffsetInBytes; + if (mIsProtectedBuffer) { + // temporary workaround until vsync event logic is corrected. + // it seems that overlay buffer update and renderring can be overlapped, + // as such encryption bit may be cleared during HW rendering + ySurface |= 0x01; + } + + switch(format) { + case HAL_PIXEL_FORMAT_YV12: // YV12 + vSurface = ySurface + yStride * h; + uSurface = vSurface + uvStride * (h / 2); + yTileOffsetX = srcX; + yTileOffsetY = srcY; + uTileOffsetX = srcX / 2; + uTileOffsetY = srcY / 2; + vTileOffsetX = uTileOffsetX; + vTileOffsetY = uTileOffsetY; + backBuffer->OCMD |= OVERLAY_FORMAT_PLANAR_YUV420; + break; + case HAL_PIXEL_FORMAT_I420: // I420 + uSurface = ySurface + yStride * h; + vSurface = uSurface + uvStride * (h / 2); + yTileOffsetX = srcX; + yTileOffsetY = srcY; + uTileOffsetX = srcX / 2; + uTileOffsetY = srcY / 2; + vTileOffsetX = uTileOffsetX; + vTileOffsetY = uTileOffsetY; + backBuffer->OCMD |= OVERLAY_FORMAT_PLANAR_YUV420; + break; + case HAL_PIXEL_FORMAT_NV12: // NV12 + uSurface = ySurface; + vSurface = ySurface; + backBuffer->OBUF_0U = yStride * h; + yTileOffsetX = srcX; + yTileOffsetY = srcY; + uTileOffsetX = srcX / 2; + uTileOffsetY = srcY / 2 + h; + vTileOffsetX = uTileOffsetX; + vTileOffsetY = uTileOffsetY; + backBuffer->OCMD |= OVERLAY_FORMAT_PLANAR_NV12_2; + break; + // NOTE: this is the decoded video format, align the height to 32B + //as it's defined by video driver + case OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar: // NV12 + uSurface = ySurface + yStride * align_to(h, 32); + vSurface = ySurface + yStride * align_to(h, 32); + yTileOffsetX = srcX; + yTileOffsetY = srcY; + uTileOffsetX = srcX; + uTileOffsetY = srcY / 2; + vTileOffsetX = uTileOffsetX; + vTileOffsetY = uTileOffsetY; + backBuffer->OCMD |= OVERLAY_FORMAT_PLANAR_NV12_2; + break; + case OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar_Tiled: //NV12_tiled + uSurface = ySurface + yStride * align_to(h, 32); + vSurface = ySurface + yStride * align_to(h, 32); + yTileOffsetX = srcX; + yTileOffsetY = srcY; + uTileOffsetX = srcX; + uTileOffsetY = srcY / 2; + vTileOffsetX = uTileOffsetX; + vTileOffsetY = uTileOffsetY; + backBuffer->OCMD |= OVERLAY_FORMAT_PLANAR_NV12_2; + backBuffer->OCMD |= OVERLAY_MEMORY_LAYOUT_TILED; + break; + case HAL_PIXEL_FORMAT_YUY2: // YUY2 + uSurface = ySurface; + vSurface = ySurface; + yTileOffsetX = srcX; + yTileOffsetY = srcY; + uTileOffsetX = yTileOffsetX; + uTileOffsetY = yTileOffsetY; + vTileOffsetX = yTileOffsetX; + vTileOffsetY = yTileOffsetY; + backBuffer->OCMD |= OVERLAY_FORMAT_PACKED_YUV422; + backBuffer->OCMD |= OVERLAY_PACKED_ORDER_YUY2; + break; + case HAL_PIXEL_FORMAT_UYVY: // UYVY + uSurface = ySurface; + vSurface = ySurface; + yTileOffsetX = srcX; + yTileOffsetY = srcY; + uTileOffsetX = yTileOffsetX; + uTileOffsetY = yTileOffsetY; + vTileOffsetX = yTileOffsetX; + vTileOffsetY = yTileOffsetY; + backBuffer->OCMD |= OVERLAY_FORMAT_PACKED_YUV422; + backBuffer->OCMD |= OVERLAY_PACKED_ORDER_UYVY; + break; + default: + ETRACE("unsupported format %d", format); + return false; + } + + backBuffer->OSTART_0Y = ySurface; + backBuffer->OSTART_0U = uSurface; + backBuffer->OSTART_0V = vSurface; + backBuffer->OBUF_0Y += srcY * yStride + srcX; + backBuffer->OBUF_0V += (srcY / 2) * uvStride + srcX; + backBuffer->OBUF_0U += (srcY / 2) * uvStride + srcX; + backBuffer->OTILEOFF_0Y = yTileOffsetY << 16 | yTileOffsetX; + backBuffer->OTILEOFF_0U = uTileOffsetY << 16 | uTileOffsetX; + backBuffer->OTILEOFF_0V = vTileOffsetY << 16 | vTileOffsetX; + + VTRACE("done. offset (%d, %d, %d)", + backBuffer->OBUF_0Y, + backBuffer->OBUF_0U, + backBuffer->OBUF_0V); + + return true; +} + +bool AnnOverlayPlane::scalingSetup(BufferMapper& mapper) +{ + int xscaleInt, xscaleFract, yscaleInt, yscaleFract; + int xscaleIntUV, xscaleFractUV; + int yscaleIntUV, yscaleFractUV; + // UV is half the size of Y -- YUV420 + int uvratio = 2; + uint32_t newval; + coeffRec xcoeffY[N_HORIZ_Y_TAPS * N_PHASES]; + coeffRec xcoeffUV[N_HORIZ_UV_TAPS * N_PHASES]; + coeffRec ycoeffY[N_VERT_Y_TAPS * N_PHASES]; + coeffRec ycoeffUV[N_VERT_UV_TAPS * N_PHASES]; + int i, j, pos; + bool scaleChanged = false; + int x, y, w, h; + int deinterlace_factor = 1; + drmModeModeInfoPtr mode = &mModeInfo; + + OverlayBackBufferBlk *backBuffer = mBackBuffer[mCurrent]->buf; + if (!backBuffer) { + ETRACE("invalid back buffer"); + return false; + } + + if (mPanelOrientation == PANEL_ORIENTATION_180) { + if (mode->hdisplay) + x = mode->hdisplay - mPosition.x - mPosition.w; + else + x = mPosition.x; + if (mode->vdisplay) + y = mode->vdisplay - mPosition.y - mPosition.h; + else + y = mPosition.y; + } else { + x = mPosition.x; + y = mPosition.y; + } + + w = mPosition.w; + h = mPosition.h; + + // check position + checkPosition(x, y, w, h); + VTRACE("final position (%d, %d, %d, %d)", x, y, w, h); + + if ((w <= 0) || (h <= 0)) { + ETRACE("invalid dst width/height"); + return false; + } + + // setup dst position + backBuffer->DWINPOS = (y << 16) | x; + backBuffer->DWINSZ = (h << 16) | w; + + uint32_t srcWidth = mapper.getCrop().w; + uint32_t srcHeight = mapper.getCrop().h; + uint32_t dstWidth = w; + uint32_t dstHeight = h; + + if (mBobDeinterlace && !mTransform) + deinterlace_factor = 2; + + VTRACE("src (%dx%d), dst (%dx%d), transform %d", + srcWidth, srcHeight, + dstWidth, dstHeight, + mTransform); + + // switch destination width/height for scale factor calculation + // for 90/270 transformation + if (mUseOverlayRotation && ((mTransform == HWC_TRANSFORM_ROT_90) || + (mTransform == HWC_TRANSFORM_ROT_270))) { + uint32_t tmp = srcHeight; + srcHeight = srcWidth; + srcWidth = tmp; + } + + // Y down-scale factor as a multiple of 4096 + if (srcWidth == dstWidth && srcHeight == dstHeight) { + xscaleFract = (1 << 12); + yscaleFract = (1 << 12) / deinterlace_factor; + } else { + xscaleFract = ((srcWidth - 1) << 12) / dstWidth; + yscaleFract = ((srcHeight - 1) << 12) / (dstHeight * deinterlace_factor); + } + + // Calculate the UV scaling factor + xscaleFractUV = xscaleFract / uvratio; + yscaleFractUV = yscaleFract / uvratio; + + + // To keep the relative Y and UV ratios exact, round the Y scales + // to a multiple of the Y/UV ratio. + xscaleFract = xscaleFractUV * uvratio; + yscaleFract = yscaleFractUV * uvratio; + + // Integer (un-multiplied) values + xscaleInt = xscaleFract >> 12; + yscaleInt = yscaleFract >> 12; + + xscaleIntUV = xscaleFractUV >> 12; + yscaleIntUV = yscaleFractUV >> 12; + + // Check scaling ratio + if (xscaleInt > INTEL_OVERLAY_MAX_SCALING_RATIO) { + ETRACE("xscaleInt > %d", INTEL_OVERLAY_MAX_SCALING_RATIO); + return false; + } + + // shouldn't get here + if (xscaleIntUV > INTEL_OVERLAY_MAX_SCALING_RATIO) { + ETRACE("xscaleIntUV > %d", INTEL_OVERLAY_MAX_SCALING_RATIO); + return false; + } + + newval = (xscaleInt << 15) | + ((xscaleFract & 0xFFF) << 3) | ((yscaleFract & 0xFFF) << 20); + if (newval != backBuffer->YRGBSCALE) { + scaleChanged = true; + backBuffer->YRGBSCALE = newval; + } + + newval = (xscaleIntUV << 15) | ((xscaleFractUV & 0xFFF) << 3) | + ((yscaleFractUV & 0xFFF) << 20); + if (newval != backBuffer->UVSCALE) { + scaleChanged = true; + backBuffer->UVSCALE = newval; + } + + newval = yscaleInt << 16 | yscaleIntUV; + if (newval != backBuffer->UVSCALEV) { + scaleChanged = true; + backBuffer->UVSCALEV = newval; + } + + // Recalculate coefficients if the scaling changed + // Only Horizontal coefficients so far. + if (scaleChanged) { + double fHCutoffY; + double fHCutoffUV; + double fVCutoffY; + double fVCutoffUV; + + fHCutoffY = xscaleFract / 4096.0; + fHCutoffUV = xscaleFractUV / 4096.0; + fVCutoffY = yscaleFract / 4096.0; + fVCutoffUV = yscaleFractUV / 4096.0; + + // Limit to between 1.0 and 3.0 + if (fHCutoffY < MIN_CUTOFF_FREQ) + fHCutoffY = MIN_CUTOFF_FREQ; + if (fHCutoffY > MAX_CUTOFF_FREQ) + fHCutoffY = MAX_CUTOFF_FREQ; + if (fHCutoffUV < MIN_CUTOFF_FREQ) + fHCutoffUV = MIN_CUTOFF_FREQ; + if (fHCutoffUV > MAX_CUTOFF_FREQ) + fHCutoffUV = MAX_CUTOFF_FREQ; + + if (fVCutoffY < MIN_CUTOFF_FREQ) + fVCutoffY = MIN_CUTOFF_FREQ; + if (fVCutoffY > MAX_CUTOFF_FREQ) + fVCutoffY = MAX_CUTOFF_FREQ; + if (fVCutoffUV < MIN_CUTOFF_FREQ) + fVCutoffUV = MIN_CUTOFF_FREQ; + if (fVCutoffUV > MAX_CUTOFF_FREQ) + fVCutoffUV = MAX_CUTOFF_FREQ; + + updateCoeff(N_HORIZ_Y_TAPS, fHCutoffY, true, true, xcoeffY); + updateCoeff(N_HORIZ_UV_TAPS, fHCutoffUV, true, false, xcoeffUV); + updateCoeff(N_VERT_Y_TAPS, fVCutoffY, false, true, ycoeffY); + updateCoeff(N_VERT_UV_TAPS, fVCutoffUV, false, false, ycoeffUV); + + for (i = 0; i < N_PHASES; i++) { + for (j = 0; j < N_HORIZ_Y_TAPS; j++) { + pos = i * N_HORIZ_Y_TAPS + j; + backBuffer->Y_HCOEFS[pos] = + (xcoeffY[pos].sign << 15 | + xcoeffY[pos].exponent << 12 | + xcoeffY[pos].mantissa); + } + } + for (i = 0; i < N_PHASES; i++) { + for (j = 0; j < N_HORIZ_UV_TAPS; j++) { + pos = i * N_HORIZ_UV_TAPS + j; + backBuffer->UV_HCOEFS[pos] = + (xcoeffUV[pos].sign << 15 | + xcoeffUV[pos].exponent << 12 | + xcoeffUV[pos].mantissa); + } + } + + for (i = 0; i < N_PHASES; i++) { + for (j = 0; j < N_VERT_Y_TAPS; j++) { + pos = i * N_VERT_Y_TAPS + j; + backBuffer->Y_VCOEFS[pos] = + (ycoeffY[pos].sign << 15 | + ycoeffY[pos].exponent << 12 | + ycoeffY[pos].mantissa); + } + } + for (i = 0; i < N_PHASES; i++) { + for (j = 0; j < N_VERT_UV_TAPS; j++) { + pos = i * N_VERT_UV_TAPS + j; + backBuffer->UV_VCOEFS[pos] = + (ycoeffUV[pos].sign << 15 | + ycoeffUV[pos].exponent << 12 | + ycoeffUV[pos].mantissa); + } + } + } + + XTRACE(); + return true; +} + +void AnnOverlayPlane::setTransform(int transform) +{ + RETURN_VOID_IF_NOT_INIT(); + + if (mPanelOrientation == PANEL_ORIENTATION_180) + transform ^= HWC_TRANSFORM_ROT_180; + + DisplayPlane::setTransform(transform); + + // setup transform config + switch (mTransform) { + case HWC_TRANSFORM_ROT_90: + mRotationConfig = (0x1 << 10); + break; + case HWC_TRANSFORM_ROT_180: + mRotationConfig = (0x2 << 10); + break; + case HWC_TRANSFORM_ROT_270: + mRotationConfig = (0x3 << 10); + break; + case 0: + mRotationConfig = 0; + break; + default: + ETRACE("Invalid transform %d", mTransform); + mRotationConfig = 0; + break; + } +} + +// HSD 4645510: +// This is a SOC limition, that when source buffer width range is +// in (960, 1024] - one cache line length, and rotation bit is set +// in portrait mode, video will show distortion. +bool AnnOverlayPlane::isSettingRotBitAllowed() +{ + uint32_t width = mSrcCrop.w; + + if ((width > 960 && width <= 1024) && + (mTransform == 0 || mTransform == HAL_TRANSFORM_ROT_180)) + return false; + return true; +} + +bool AnnOverlayPlane::flip(void *ctx) +{ + uint32_t ovadd = 0; + + RETURN_FALSE_IF_NOT_INIT(); + + if (!DisplayPlane::flip(ctx)) { + ETRACE("failed to flip display plane."); + return false; + } + + // update back buffer address + ovadd = (mBackBuffer[mCurrent]->gttOffsetInPage << 12); + + // enable rotation mode and setup rotation config + // if video is interlaced, cannot use overlay rotation + if (mIndex == 0 && !mBobDeinterlace) { + if (isSettingRotBitAllowed()) + ovadd |= (1 << 12); + ovadd |= mRotationConfig; + } + + // setup z-order config + ovadd |= mZOrderConfig; + + // load coefficients + ovadd |= 0x1; + + // enable overlay + ovadd |= (1 << 15); + + mContext.type = DC_OVERLAY_PLANE; + mContext.ctx.ov_ctx.ovadd = ovadd; + mContext.ctx.ov_ctx.index = mIndex; + mContext.ctx.ov_ctx.pipe = mDevice; + mContext.ctx.ov_ctx.ovadd |= mPipeConfig; + + // move to next back buffer + mCurrent = (mCurrent + 1) % OVERLAY_BACK_BUFFER_COUNT; + + VTRACE("ovadd = %#x, index = %d, device = %d", + mContext.ctx.ov_ctx.ovadd, + mIndex, + mDevice); + + return true; +} + +void* AnnOverlayPlane::getContext() const +{ + CTRACE(); + return (void *)&mContext; +} + +bool AnnOverlayPlane::setDataBuffer(BufferMapper& mapper) +{ + if (OverlayPlaneBase::setDataBuffer(mapper) == false) { + return false; + } + + signalVideoRotation(mapper); + + if (mIsProtectedBuffer) { + // Bit 0: Decryption request, only allowed to change on a synchronous flip + // This request will be qualified with the separate decryption enable bit for OV + mBackBuffer[mCurrent]->buf->OSTART_0Y |= 0x1; + mBackBuffer[mCurrent]->buf->OSTART_1Y |= 0x1; + } + + mContext.gtt_key = (unsigned long)mapper.getCpuAddress(0); + + return true; +} + +bool AnnOverlayPlane::initialize(uint32_t bufferCount) +{ + if (!OverlayPlaneBase::initialize(bufferCount)) { + ETRACE("failed to initialize OverlayPlaneBase"); + return false; + } + + // setup rotation buffer + mRotationBufProvider = new RotationBufferProvider(mWsbm); + if (!mRotationBufProvider || !mRotationBufProvider->initialize()) { + DEINIT_AND_RETURN_FALSE("failed to initialize RotationBufferProvider"); + } + return true; +} + +void AnnOverlayPlane::deinitialize() +{ + DEINIT_AND_DELETE_OBJ(mRotationBufProvider); + OverlayPlaneBase::deinitialize(); +} + +bool AnnOverlayPlane::rotatedBufferReady(BufferMapper& mapper, BufferMapper* &rotatedMapper) +{ + struct VideoPayloadBuffer *payload; + uint32_t format; + // only NV12_VED has rotated buffer + format = mapper.getFormat(); + if (format != OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar && + format != OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar_Tiled) { + ETRACE("invalid video format %#x", format); + return false; + } + + payload = (struct VideoPayloadBuffer *)mapper.getCpuAddress(SUB_BUFFER1); + // check payload + if (!payload) { + ETRACE("no payload found"); + return false; + } + + if (payload->force_output_method == FORCE_OUTPUT_GPU) { + ETRACE("Output method is not supported!"); + return false; + } + + if (payload->client_transform != mTransform || + mBobDeinterlace) { + if (!mRotationBufProvider->setupRotationBuffer(payload, mTransform)) { + DTRACE("failed to setup rotation buffer"); + return false; + } + } + + rotatedMapper = getTTMMapper(mapper, payload); + return true; +} + +void AnnOverlayPlane::signalVideoRotation(BufferMapper& mapper) +{ + struct VideoPayloadBuffer *payload; + uint32_t format; + + // check if it's video layer + format = mapper.getFormat(); + if (format != OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar && + format != OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar_Tiled) { + return; + } + + payload = (struct VideoPayloadBuffer *)mapper.getCpuAddress(SUB_BUFFER1); + if (!payload) { + ETRACE("no payload found"); + return; + } + + /* if use overlay rotation, signal decoder to stop rotation */ + if (mUseOverlayRotation) { + if (payload->client_transform) { + WTRACE("signal decoder to stop generate rotation buffer"); + payload->hwc_timestamp = systemTime(); + payload->layer_transform = 0; + } + } else { + /* if overlay rotation cannot be used, signal decoder to start rotation */ + if (payload->client_transform != mTransform) { + WTRACE("signal decoder to generate rotation buffer with transform %d", mTransform); + payload->hwc_timestamp = systemTime(); + payload->layer_transform = mTransform; + } + } +} + +bool AnnOverlayPlane::useOverlayRotation(BufferMapper& mapper) +{ + if (mTransform == 0) + return true; + + if (!isSettingRotBitAllowed()) { + mUseOverlayRotation = false; + mRotationConfig = 0; + return false; + } + + // workaround limitation of overlay rotation by falling back to use VA rotated buffer + bool fallback = false; + float scaleX = (float)mSrcCrop.w / mPosition.w; + float scaleY = (float)mSrcCrop.h / mPosition.h; + if (mTransform == HAL_TRANSFORM_ROT_270 || mTransform == HAL_TRANSFORM_ROT_90) { + scaleX = (float)mSrcCrop.w / mPosition.h; + scaleY = (float)mSrcCrop.h / mPosition.w; + } + if (scaleX >= 3 || scaleY >= 3) { + if (mUseOverlayRotation) { + DTRACE("overlay rotation with scaling >= 3, use VA rotated buffer"); + } + fallback = true; + } else if ((int)mSrcCrop.x & 63) { + if (mUseOverlayRotation) { + DTRACE("offset is not 64 bytes aligned, use VA rotated buffer"); + } + fallback = true; + } +#if 0 + else if (mTransform != HAL_TRANSFORM_ROT_180 && scaleX != scaleY) { + if (mUseOverlayRotation) { + DTRACE("overlay rotation with uneven scaling, use VA rotated buffer"); + } + fallback = true; + } +#endif + + // per DC spec, if video is 1080(H)x1920(V), the buffer + // need 1920 of 64-pixel strip if using hw rotation. + // fallback to video ration buffer in such case. + if (mSrcCrop.w == 1080 && mSrcCrop.h == 1920 && mTransform != 0) { + DTRACE("1080(H)x1920(V) cannot use hw rotation, use VA rotated buffer"); + fallback = true; + } + + if (fallback || mBobDeinterlace) { + mUseOverlayRotation = false; + mRotationConfig = 0; + } else { + mUseOverlayRotation = true; + } + return mUseOverlayRotation; +} + +bool AnnOverlayPlane::scaledBufferReady(BufferMapper& mapper, BufferMapper* &scaledMapper, VideoPayloadBuffer *payload) +{ + mUseScaledBuffer = (payload->scaling_khandle != 0); + + if (mUseScaledBuffer) { + mapper.setCrop(mapper.getCrop().x, mapper.getCrop().y, payload->scaling_width, payload->scaling_height); + scaledMapper = getTTMMapper(mapper, payload); + return (scaledMapper != 0); + } + + return mUseScaledBuffer; +} + +bool AnnOverlayPlane::flush(uint32_t flags) +{ + RETURN_FALSE_IF_NOT_INIT(); + ATRACE("flags = %#x, type = %d, index = %d", flags, mType, mIndex); + + if (!(flags & PLANE_ENABLE) && !(flags & PLANE_DISABLE)) { + ETRACE("invalid flush flags."); + return false; + } + + struct drm_psb_register_rw_arg arg; + memset(&arg, 0, sizeof(struct drm_psb_register_rw_arg)); + + if (flags & PLANE_DISABLE) + arg.plane_disable_mask = 1; + else if (flags & PLANE_ENABLE) + arg.plane_enable_mask = 1; + + arg.plane.type = DC_OVERLAY_PLANE; + arg.plane.index = mIndex; + arg.plane.ctx = mContext.ctx.ov_ctx.ovadd; + if (flags & PLANE_DISABLE) { + DTRACE("disabling overlay %d on device %d", mIndex, mDevice); + } + + // issue ioctl + Drm *drm = Hwcomposer::getInstance().getDrm(); + bool ret = drm->writeReadIoctl(DRM_PSB_REGISTER_RW, &arg, sizeof(arg)); + if (ret == false) { + WTRACE("overlay update failed with error code %d", ret); + return false; + } + + return true; +} + +} // namespace intel +} // namespace android diff --git a/merrifield/ips/anniedale/AnnOverlayPlane.h b/merrifield/ips/anniedale/AnnOverlayPlane.h new file mode 100644 index 0000000..3cea338 --- /dev/null +++ b/merrifield/ips/anniedale/AnnOverlayPlane.h @@ -0,0 +1,78 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef ANN_OVERLAY_PLANE_H +#define ANN_OVERLAY_PLANE_H + +#include <utils/KeyedVector.h> +#include <hal_public.h> +#include <DisplayPlane.h> +#include <BufferMapper.h> +#include <common/Wsbm.h> +#include <common/OverlayPlaneBase.h> +#include <common/RotationBufferProvider.h> + +namespace android { +namespace intel { + +class AnnOverlayPlane : public OverlayPlaneBase { +public: + AnnOverlayPlane(int index, int disp); + virtual ~AnnOverlayPlane(); + + virtual void setTransform(int transform); + virtual void setZOrderConfig(ZOrderConfig& config, void *nativeConfig); + + // plane operations + virtual bool flip(void *ctx); + virtual bool reset(); + virtual bool enable(); + virtual bool disable(); + virtual void postFlip(); + virtual void* getContext() const; + virtual bool initialize(uint32_t bufferCount); + virtual void deinitialize(); + virtual bool rotatedBufferReady(BufferMapper& mapper, BufferMapper* &rotatedMapper); + virtual bool useOverlayRotation(BufferMapper& mapper); + virtual bool scaledBufferReady(BufferMapper& mapper, BufferMapper* &scaledMapper, VideoPayloadBuffer *payload); + +private: + void signalVideoRotation(BufferMapper& mapper); + bool isSettingRotBitAllowed(); + +protected: + virtual bool setDataBuffer(BufferMapper& mapper); + virtual bool flush(uint32_t flags); + virtual bool bufferOffsetSetup(BufferMapper& mapper); + virtual bool scalingSetup(BufferMapper& mapper); + + virtual void resetBackBuffer(int buf); + + RotationBufferProvider *mRotationBufProvider; + + // rotation config + uint32_t mRotationConfig; + // z order config + uint32_t mZOrderConfig; + bool mUseOverlayRotation; + // hardware context + struct intel_dc_plane_ctx mContext; +}; + +} // namespace intel +} // namespace android + +#endif /* ANN_OVERLAY_PLANE_H */ + diff --git a/merrifield/ips/anniedale/AnnPlaneManager.cpp b/merrifield/ips/anniedale/AnnPlaneManager.cpp new file mode 100644 index 0000000..b20f851 --- /dev/null +++ b/merrifield/ips/anniedale/AnnPlaneManager.cpp @@ -0,0 +1,461 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <HwcTrace.h> +#include <utils/String8.h> +#include <anniedale/AnnPlaneManager.h> +#include <anniedale/AnnRGBPlane.h> +#include <anniedale/AnnOverlayPlane.h> +#include <anniedale/AnnCursorPlane.h> +#include <PlaneCapabilities.h> + +namespace android { +namespace intel { + + +struct PlaneDescription { + char nickname; + int type; + int index; +}; + + +static PlaneDescription PLANE_DESC[] = +{ + // nickname must be continous and start with 'A', + // it is used to fast locate plane index and type + {'A', DisplayPlane::PLANE_PRIMARY, 0}, + {'B', DisplayPlane::PLANE_PRIMARY, 1}, + {'C', DisplayPlane::PLANE_PRIMARY, 2}, + {'D', DisplayPlane::PLANE_SPRITE, 0}, + {'E', DisplayPlane::PLANE_SPRITE, 1}, + {'F', DisplayPlane::PLANE_SPRITE, 2}, + {'G', DisplayPlane::PLANE_OVERLAY, 0}, // nickname for Overlay A + {'H', DisplayPlane::PLANE_OVERLAY, 1}, // nickname for Overlay C + {'I', DisplayPlane::PLANE_CURSOR, 0}, // nickname for cursor A + {'J', DisplayPlane::PLANE_CURSOR, 1}, // nickname for cursor B + {'K', DisplayPlane::PLANE_CURSOR, 2} // nickname for cursor C +}; + + +struct ZOrderDescription { + int index; // based on overlay position + const char *zorder; +}; + +// If overlay is in the bottom of Z order, two legitimate combinations are Oa, D, E, F +// and Oc, D, E, F. However, plane A has to be part of the blending chain as it can't +// be disabled [HW bug]. The only legitimate combinations including overlay and plane A is: +// A, Oa, E, F +// A, Oc, E, F +// Cursor plane can be placed on top of any plane below and is intentionally ignored +// in the zorder table. + +// video mode panel doesn't need the primay plane A always on hack +static ZOrderDescription PIPE_A_ZORDER_DESC_VID[] = +{ + {0, "ADEF"}, // no overlay + {1, "GDEF"}, // overlay A at bottom (1 << 0) + {1, "HDEF"}, // overlay C at bottom (1 << 0) + {2, "AGEF"}, // overlay A at next to bottom (1 << 1) + {2, "AHEF"}, // overlay C at next to bottom (1 << 1) + {3, "GHEF"}, // overlay A, C at bottom + {4, "ADGF"}, // overlay A at next to top (1 << 2) + {4, "ADHF"}, // overlay C at next to top (1 << 2) + {6, "AGHF"}, // overlay A, C in between + {8, "ADEG"}, // overlay A at top (1 << 3) + {8, "ADEH"}, // overlay C at top (1 <<3) + {12, "ADGH"} // overlay A, C at top +}; + +static ZOrderDescription PIPE_A_ZORDER_DESC_CMD[] = +{ + {0, "ADEF"}, // no overlay + {1, "GEF"}, // overlay A at bottom (1 << 0) + {1, "HEF"}, // overlay C at bottom (1 << 0) + {2, "AGEF"}, // overlay A at next to bottom (1 << 1) + {2, "AHEF"}, // overlay C at next to bottom (1 << 1) + {3, "GHF"}, // overlay A, C at bottom + {4, "ADGF"}, // overlay A at next to top (1 << 2) + {4, "ADHF"}, // overlay C at next to top (1 << 2) + {6, "AGHF"}, // overlay A, C in between + {8, "ADEG"}, // overlay A at top (1 << 3) + {8, "ADEH"}, // overlay C at top (1 <<3) + {12, "ADGH"} // overlay A, C at top +}; + +// use overlay C over overlay A if possible on pipe B +static ZOrderDescription PIPE_B_ZORDER_DESC[] = +{ + {0, "BD"}, // no overlay + {1, "HBD"}, // overlay C at bottom (1 << 0) +// {1, "GBD"}, // overlay A at bottom (1 << 0), overlay A don`t switch to pipeB and only overlay C on pipeB + {2, "BHD"}, // overlay C at middle (1 << 1) +// {2, "BGD"}, // overlay A at middle (1 << 1), overlay A don`t switch to pipeB and only overaly C on pipeB + {3, "GHBD"}, // overlay A and C at bottom ( 1 << 0 + 1 << 1) + {4, "BDH"}, // overlay C at top (1 << 2) + {4, "BDG"}, // overlay A at top (1 << 2) + {6, "BGHD"}, // overlay A/C at middle 1 << 1 + 1 << 2) + {12, "BDGH"} // overlay A/C at top (1 << 2 + 1 << 3) +}; + +static ZOrderDescription *PIPE_A_ZORDER_TBL; +static int PIPE_A_ZORDER_COMBINATIONS; +static ZOrderDescription *PIPE_B_ZORDER_TBL; +static int PIPE_B_ZORDER_COMBINATIONS; +static bool OVERLAY_HW_WORKAROUND; + +AnnPlaneManager::AnnPlaneManager() + : DisplayPlaneManager() +{ +} + +AnnPlaneManager::~AnnPlaneManager() +{ +} + +bool AnnPlaneManager::initialize() +{ + mSpritePlaneCount = 3; // Sprite D, E, F + mOverlayPlaneCount = 2; // Overlay A, C + mPrimaryPlaneCount = 3; // Primary A, B, C + mCursorPlaneCount = 3; + + uint32_t videoMode = 0; + Drm *drm = Hwcomposer::getInstance().getDrm(); + drm->readIoctl(DRM_PSB_PANEL_QUERY, &videoMode, sizeof(uint32_t)); + if (videoMode == 1) { + DTRACE("video mode panel, no primay A always on hack"); + PIPE_A_ZORDER_TBL = PIPE_A_ZORDER_DESC_VID; + PIPE_A_ZORDER_COMBINATIONS = + sizeof(PIPE_A_ZORDER_DESC_VID)/sizeof(ZOrderDescription); + } else { + DTRACE("command mode panel, need primay A always on hack"); + PIPE_A_ZORDER_TBL = PIPE_A_ZORDER_DESC_CMD; + PIPE_A_ZORDER_COMBINATIONS = + sizeof(PIPE_A_ZORDER_DESC_CMD)/sizeof(ZOrderDescription); + OVERLAY_HW_WORKAROUND = true; + } + + PIPE_B_ZORDER_TBL = PIPE_B_ZORDER_DESC; + PIPE_B_ZORDER_COMBINATIONS = + sizeof(PIPE_B_ZORDER_DESC)/sizeof(ZOrderDescription); + + return DisplayPlaneManager::initialize(); +} + +void AnnPlaneManager::deinitialize() +{ + DisplayPlaneManager::deinitialize(); +} + +DisplayPlane* AnnPlaneManager::allocPlane(int index, int type) +{ + DisplayPlane *plane = NULL; + + switch (type) { + case DisplayPlane::PLANE_PRIMARY: + plane = new AnnRGBPlane(index, DisplayPlane::PLANE_PRIMARY, index/*disp*/); + break; + case DisplayPlane::PLANE_SPRITE: + plane = new AnnRGBPlane(index, DisplayPlane::PLANE_SPRITE, 0/*disp*/); + break; + case DisplayPlane::PLANE_OVERLAY: + plane = new AnnOverlayPlane(index, 0/*disp*/); + break; + case DisplayPlane::PLANE_CURSOR: + plane = new AnnCursorPlane(index, index /*disp */); + break; + default: + ETRACE("unsupported type %d", type); + break; + } + + if (plane && !plane->initialize(DisplayPlane::MIN_DATA_BUFFER_COUNT)) { + ETRACE("failed to initialize plane."); + DEINIT_AND_DELETE_OBJ(plane); + } + + return plane; +} + +bool AnnPlaneManager::isValidZOrder(int dsp, ZOrderConfig& config) +{ + int size = (int)config.size(); + bool hasCursor = false; + + for (int i = 0; i < size; i++) { + if (config[i]->planeType == DisplayPlane::PLANE_CURSOR) { + hasCursor = true; + break; + } + } + + if (size <= 0 || + (hasCursor && size > 5) || + (!hasCursor && size > 4)) { + VTRACE("invalid z order config size %d", size); + return false; + } + + if (dsp == IDisplayDevice::DEVICE_PRIMARY) { + int firstOverlay = -1; + for (int i = 0; i < size; i++) { + if (config[i]->planeType == DisplayPlane::PLANE_OVERLAY) { + firstOverlay = i; + break; + } + } + + int sprites = 0; + for (int i = 0; i < size; i++) { + if (config[i]->planeType != DisplayPlane::PLANE_OVERLAY && + config[i]->planeType != DisplayPlane::PLANE_CURSOR) { + sprites++; + } + } + + if (firstOverlay < 0 && sprites > 4) { + VTRACE("not capable to support more than 4 sprite layers"); + return false; + } + + if (OVERLAY_HW_WORKAROUND) { + if (firstOverlay == 0 && size > 2) { + VTRACE("can not support 3 sprite layers on top of overlay"); + return false; + } + } + } else if (dsp == IDisplayDevice::DEVICE_EXTERNAL) { + int sprites = 0; + for (int i = 0; i < size; i++) { + if (config[i]->planeType != DisplayPlane::PLANE_OVERLAY && + config[i]->planeType != DisplayPlane::PLANE_CURSOR) { + sprites++; + } + } + if (sprites > 2) { + ETRACE("number of sprite: %d, maximum 1 sprite and 1 primary supported on pipe 1", sprites); + return false; + } + } else { + ETRACE("invalid display device %d", dsp); + return false; + } + return true; +} + +bool AnnPlaneManager::assignPlanes(int dsp, ZOrderConfig& config) +{ + if (dsp < 0 || dsp > IDisplayDevice::DEVICE_EXTERNAL) { + ETRACE("invalid display device %d", dsp); + return false; + } + + int size = (int)config.size(); + + // calculate index based on overlay Z order position + int index = 0; + for (int i = 0; i < size; i++) { + if (config[i]->planeType == DisplayPlane::PLANE_OVERLAY) { + index += (1 << i); + } + } + + int combinations; + ZOrderDescription *table; + if (dsp == IDisplayDevice::DEVICE_PRIMARY) { + combinations = PIPE_A_ZORDER_COMBINATIONS; + table = PIPE_A_ZORDER_TBL; + } else { + combinations = PIPE_B_ZORDER_COMBINATIONS; + table = PIPE_B_ZORDER_TBL; + } + + for (int i = 0; i < combinations; i++) { + ZOrderDescription *zorderDesc = table + i; + + if (zorderDesc->index != index) + continue; + + if (assignPlanes(dsp, config, zorderDesc->zorder)) { + VTRACE("zorder assigned %s", zorderDesc->zorder); + return true; + } + } + return false; +} + +bool AnnPlaneManager::assignPlanes(int dsp, ZOrderConfig& config, const char *zorder) +{ + // zorder string does not include cursor plane, therefore cursor layer needs to be handled + // in a special way. Cursor layer must be on top of zorder and no more than one cursor layer. + + int size = (int)config.size(); + if (zorder == NULL || size == 0) { + //DTRACE("invalid zorder or ZOrder config."); + return false; + } + + int zorderLen = (int)strlen(zorder); + + // test if plane is avalable + for (int i = 0; i < size; i++) { + if (config[i]->planeType == DisplayPlane::PLANE_CURSOR) { + if (i != size - 1) { + ETRACE("invalid zorder of cursor layer"); + return false; + } + PlaneDescription& desc = PLANE_DESC['I' - 'A' + dsp]; + if (!isFreePlane(desc.type, desc.index)) { + ETRACE("cursor plane is not available"); + return false; + } + continue; + } + if (i >= zorderLen) { + DTRACE("index of ZOrderConfig is out of bound"); + return false; + } + + char id = *(zorder + i); + PlaneDescription& desc = PLANE_DESC[id - 'A']; + if (!isFreePlane(desc.type, desc.index)) { + DTRACE("plane type %d index %d is not available", desc.type, desc.index); + return false; + } + +#if 0 + // plane type check + if (config[i]->planeType == DisplayPlane::PLANE_OVERLAY && + desc.type != DisplayPlane::PLANE_OVERLAY) { + ETRACE("invalid plane type %d, expected %d", desc.type, config[i]->planeType); + return false; + } + + if (config[i]->planeType != DisplayPlane::PLANE_OVERLAY) { + if (config[i]->planeType != DisplayPlane::PLANE_PRIMARY && + config[i]->planeType != DisplayPlane::PLANE_SPRITE) { + ETRACE("invalid plane type %d,", config[i]->planeType); + return false; + } + if (desc.type != DisplayPlane::PLANE_PRIMARY && + desc.type != DisplayPlane::PLANE_SPRITE) { + ETRACE("invalid plane type %d, expected %d", desc.type, config[i]->planeType); + return false; + } + } +#endif + + if (desc.type == DisplayPlane::PLANE_OVERLAY && desc.index == 1 && + config[i]->hwcLayer->getTransform() != 0) { + DTRACE("overlay C does not support transform"); + return false; + } + } + + bool primaryPlaneActive = false; + // allocate planes + for (int i = 0; i < size; i++) { + if (config[i]->planeType == DisplayPlane::PLANE_CURSOR) { + PlaneDescription& desc = PLANE_DESC['I' - 'A' + dsp]; + ZOrderLayer *zLayer = config.itemAt(i); + zLayer->plane = getPlane(desc.type, desc.index); + if (zLayer->plane == NULL) { + ETRACE("failed to get cursor plane, should never happen!"); + } + continue; + } + + char id = *(zorder + i); + PlaneDescription& desc = PLANE_DESC[id - 'A']; + ZOrderLayer *zLayer = config.itemAt(i); + zLayer->plane = getPlane(desc.type, desc.index); + if (zLayer->plane == NULL) { + ETRACE("failed to get plane, should never happen!"); + } + // override type + zLayer->planeType = desc.type; + if (desc.type == DisplayPlane::PLANE_PRIMARY) { + primaryPlaneActive = true; + } + } + + // setup Z order + int slot = 0; + for (int i = 0; i < size; i++) { + slot = i; + + if (OVERLAY_HW_WORKAROUND) { + if (!primaryPlaneActive && + config[i]->planeType == DisplayPlane::PLANE_OVERLAY) { + slot += 1; + } + } + + config[i]->plane->setZOrderConfig(config, (void *)(unsigned long)slot); + config[i]->plane->enable(); + } + +#if 0 + DTRACE("config size %d, zorder %s", size, zorder); + for (int i = 0; i < size; i++) { + const ZOrderLayer *l = config.itemAt(i); + ITRACE("%d: plane type %d, index %d, zorder %d", + i, l->planeType, l->plane->getIndex(), l->zorder); + } +#endif + + return true; +} + +void* AnnPlaneManager::getZOrderConfig() const +{ + return NULL; +} + +int AnnPlaneManager::getFreePlanes(int dsp, int type) +{ + RETURN_NULL_IF_NOT_INIT(); + + if (type != DisplayPlane::PLANE_SPRITE) { + return DisplayPlaneManager::getFreePlanes(dsp, type); + } + + if (dsp < 0 || dsp > IDisplayDevice::DEVICE_EXTERNAL) { + ETRACE("invalid display device %d", dsp); + return 0; + } + + uint32_t freePlanes = mFreePlanes[type] | mReclaimedPlanes[type]; + int start = 0; + int stop = mSpritePlaneCount; + if (dsp == IDisplayDevice::DEVICE_EXTERNAL) { + // only Sprite D (index 0) can be assigned to pipe 1 + // Sprites E/F (index 1, 2) are fixed on pipe 0 + stop = 1; + } + int count = 0; + for (int i = start; i < stop; i++) { + if ((1 << i) & freePlanes) { + count++; + } + } + return count; +} + +} // namespace intel +} // namespace android + diff --git a/merrifield/ips/anniedale/AnnPlaneManager.h b/merrifield/ips/anniedale/AnnPlaneManager.h new file mode 100644 index 0000000..5a7971e --- /dev/null +++ b/merrifield/ips/anniedale/AnnPlaneManager.h @@ -0,0 +1,48 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef ANN_PLANE_MANAGER_H +#define ANN_PLANE_MANAGER_H + +#include <DisplayPlaneManager.h> +#include <linux/psb_drm.h> + +namespace android { +namespace intel { + +class AnnPlaneManager : public DisplayPlaneManager { +public: + AnnPlaneManager(); + virtual ~AnnPlaneManager(); + +public: + virtual bool initialize(); + virtual void deinitialize(); + virtual bool isValidZOrder(int dsp, ZOrderConfig& config); + virtual bool assignPlanes(int dsp, ZOrderConfig& config); + virtual int getFreePlanes(int dsp, int type); + // TODO: remove this API + virtual void* getZOrderConfig() const; + +protected: + DisplayPlane* allocPlane(int index, int type); + bool assignPlanes(int dsp, ZOrderConfig& config, const char *zorder); +}; + +} // namespace intel +} // namespace android + + +#endif /* ANN_PLANE_MANAGER_H */ diff --git a/merrifield/ips/anniedale/AnnRGBPlane.cpp b/merrifield/ips/anniedale/AnnRGBPlane.cpp new file mode 100644 index 0000000..dfc4b8c --- /dev/null +++ b/merrifield/ips/anniedale/AnnRGBPlane.cpp @@ -0,0 +1,335 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <HwcTrace.h> +#include <Hwcomposer.h> +#include <BufferManager.h> +#include <anniedale/AnnRGBPlane.h> +#include <tangier/TngGrallocBuffer.h> +#include <common/PixelFormat.h> + +namespace android { +namespace intel { + +AnnRGBPlane::AnnRGBPlane(int index, int type, int disp) + : DisplayPlane(index, type, disp) +{ + CTRACE(); + memset(&mContext, 0, sizeof(mContext)); +} + +AnnRGBPlane::~AnnRGBPlane() +{ + CTRACE(); +} + +bool AnnRGBPlane::enable() +{ + return enablePlane(true); +} + +bool AnnRGBPlane::disable() +{ + return enablePlane(false); +} + +void* AnnRGBPlane::getContext() const +{ + CTRACE(); + return (void *)&mContext; +} + +void AnnRGBPlane::setZOrderConfig(ZOrderConfig& config, void *nativeConfig) +{ + CTRACE(); +} + +bool AnnRGBPlane::setDataBuffer(buffer_handle_t handle) +{ + if (!handle) { + setFramebufferTarget(handle); + return true; + } + + TngGrallocBuffer tmpBuf(handle); + uint32_t usage; + bool ret; + + ATRACE("handle = %#x", handle); + + usage = tmpBuf.getUsage(); + if (GRALLOC_USAGE_HW_FB & usage) { + setFramebufferTarget(handle); + return true; + } + + // use primary as a sprite + ret = DisplayPlane::setDataBuffer(handle); + if (ret == false) { + ETRACE("failed to set data buffer"); + return ret; + } + + return true; +} + +bool AnnRGBPlane::setDataBuffer(BufferMapper& mapper) +{ + int bpp; + int srcX, srcY, srcW, srcH; + int dstX, dstY, dstW, dstH; + uint32_t spriteFormat; + uint32_t stride; + uint32_t linoff; + uint32_t planeAlpha; + drmModeModeInfoPtr mode = &mModeInfo; + + CTRACE(); + + // setup plane position + dstX = mPosition.x; + dstY = mPosition.y; + dstW = mPosition.w; + dstH = mPosition.h; + + checkPosition(dstX, dstY, dstW, dstH); + + // setup plane format + if (!PixelFormat::convertFormat(mapper.getFormat(), spriteFormat, bpp)) { + ETRACE("unsupported format %#x", mapper.getFormat()); + return false; + } + + // setup stride and source buffer crop + srcX = mapper.getCrop().x; + srcY = mapper.getCrop().y; + srcW = mapper.getWidth(); + srcH = mapper.getHeight(); + stride = mapper.getStride().rgb.stride; + + if (mPanelOrientation == PANEL_ORIENTATION_180) + linoff = srcY * stride + srcX * bpp + (mapper.getCrop().h - 1) * stride + (mapper.getCrop().w - 1) * bpp; + else + linoff = srcY * stride + srcX * bpp; + + // unlikely happen, but still we need make sure linoff is valid + if (linoff > (stride * mapper.getHeight())) { + ETRACE("invalid source crop"); + return false; + } + + // update context + if (mType == PLANE_SPRITE) + mContext.type = DC_SPRITE_PLANE; + else + mContext.type = DC_PRIMARY_PLANE; + + // setup plane alpha + if (0 < mPlaneAlpha && mPlaneAlpha < 0xff) { + planeAlpha = mPlaneAlpha | 0x80000000; + } else { + // disable plane alpha to offload HW + planeAlpha = 0xff; + } + + mContext.ctx.sp_ctx.index = mIndex; + mContext.ctx.sp_ctx.pipe = mDevice; + mContext.ctx.sp_ctx.cntr = spriteFormat | 0x80000000; + mContext.ctx.sp_ctx.linoff = linoff; + mContext.ctx.sp_ctx.stride = stride; + + // turn off premultipled alpha blending for HWC_BLENDING_COVERAGE + if (mBlending == HWC_BLENDING_COVERAGE) { + mContext.ctx.sp_ctx.cntr |= (0x1 << 23); + } + + if (mPanelOrientation == PANEL_ORIENTATION_180) + mContext.ctx.sp_ctx.cntr |= (0x1 << 15); + + if (mapper.isCompression()) { + mContext.ctx.sp_ctx.stride = align_to(srcW, 32) * 4; + mContext.ctx.sp_ctx.linoff = (align_to(srcW, 32) * srcH / 64) - 1; + mContext.ctx.sp_ctx.tileoff = (srcY & 0xfff) << 16 | (srcX & 0xfff); + mContext.ctx.sp_ctx.cntr |= (0x1 << 11); + } + + mContext.ctx.sp_ctx.surf = mapper.getGttOffsetInPage(0) << 12; + mContext.gtt_key = (uint64_t)mapper.getCpuAddress(0); + + if (mPanelOrientation == PANEL_ORIENTATION_180) { + if (mode->vdisplay && mode->hdisplay) + mContext.ctx.sp_ctx.pos = ((mode->vdisplay - dstY - dstH) & 0xfff) << 16 | ((mode->hdisplay - dstX - dstW) & 0xfff); + else + mContext.ctx.sp_ctx.pos = (dstY & 0xfff) << 16 | (dstX & 0xfff); + } else { + mContext.ctx.sp_ctx.pos = (dstY & 0xfff) << 16 | (dstX & 0xfff); + } + + mContext.ctx.sp_ctx.size = + ((dstH - 1) & 0xfff) << 16 | ((dstW - 1) & 0xfff); + mContext.ctx.sp_ctx.contalpa = planeAlpha; + mContext.ctx.sp_ctx.update_mask = SPRITE_UPDATE_ALL; + + VTRACE("type = %d, index = %d, cntr = %#x, linoff = %#x, stride = %#x," + "surf = %#x, pos = %#x, size = %#x, contalpa = %#x", mType, mIndex, + mContext.ctx.sp_ctx.cntr, + mContext.ctx.sp_ctx.linoff, + mContext.ctx.sp_ctx.stride, + mContext.ctx.sp_ctx.surf, + mContext.ctx.sp_ctx.pos, + mContext.ctx.sp_ctx.size, + mContext.ctx.sp_ctx.contalpa); + return true; +} + +bool AnnRGBPlane::enablePlane(bool enabled) +{ + RETURN_FALSE_IF_NOT_INIT(); + + struct drm_psb_register_rw_arg arg; + memset(&arg, 0, sizeof(struct drm_psb_register_rw_arg)); + if (enabled) { + arg.plane_enable_mask = 1; + } else { + arg.plane_disable_mask = 1; + } + + if (mType == PLANE_SPRITE) + arg.plane.type = DC_SPRITE_PLANE; + else + arg.plane.type = DC_PRIMARY_PLANE; + + arg.plane.index = mIndex; + arg.plane.ctx = 0; + + // issue ioctl + Drm *drm = Hwcomposer::getInstance().getDrm(); + bool ret = drm->writeReadIoctl(DRM_PSB_REGISTER_RW, &arg, sizeof(arg)); + if (ret == false) { + WTRACE("plane enabling (%d) failed with error code %d", enabled, ret); + return false; + } + + return true; +} + +bool AnnRGBPlane::isDisabled() +{ + RETURN_FALSE_IF_NOT_INIT(); + + struct drm_psb_register_rw_arg arg; + memset(&arg, 0, sizeof(struct drm_psb_register_rw_arg)); + + if (mType == PLANE_SPRITE) + arg.plane.type = DC_SPRITE_PLANE; + else + arg.plane.type = DC_PRIMARY_PLANE; + + arg.get_plane_state_mask = 1; + arg.plane.index = mIndex; + arg.plane.ctx = 0; + + // issue ioctl + Drm *drm = Hwcomposer::getInstance().getDrm(); + bool ret = drm->writeReadIoctl(DRM_PSB_REGISTER_RW, &arg, sizeof(arg)); + if (ret == false) { + WTRACE("plane state query failed with error code %d", ret); + return false; + } + + return arg.plane.ctx == PSB_DC_PLANE_DISABLED; +} + +void AnnRGBPlane::postFlip() +{ + // prevent mUpdateMasks from being reset + // skipping flip may cause flicking +} + +void AnnRGBPlane::setFramebufferTarget(buffer_handle_t handle) +{ + uint32_t stride; + uint32_t planeAlpha; + + CTRACE(); + + // do not need to update the buffer handle + if (mCurrentDataBuffer != handle) + mUpdateMasks |= PLANE_BUFFER_CHANGED; + else + mUpdateMasks &= ~PLANE_BUFFER_CHANGED; + + // if no update then do Not need set data buffer + if (!mUpdateMasks) + return; + + // don't need to map data buffer for primary plane + if (mType == PLANE_SPRITE) + mContext.type = DC_SPRITE_PLANE; + else + mContext.type = DC_PRIMARY_PLANE; + + stride = align_to((4 * align_to(mPosition.w, 32)), 64); + + if (0 < mPlaneAlpha && mPlaneAlpha < 0xff) { + planeAlpha = mPlaneAlpha | 0x80000000; + } else { + // disable plane alpha to offload HW + planeAlpha = 0xff; + } + + // FIXME: use sprite context for sprite plane + mContext.ctx.prim_ctx.update_mask = SPRITE_UPDATE_ALL; + mContext.ctx.prim_ctx.index = mIndex; + mContext.ctx.prim_ctx.pipe = mDevice; + + if (mPanelOrientation == PANEL_ORIENTATION_180) + mContext.ctx.prim_ctx.linoff = (mPosition.h - 1) * stride + (mPosition.w - 1) * 4; + else + mContext.ctx.prim_ctx.linoff = 0; + + mContext.ctx.prim_ctx.stride = stride; + mContext.ctx.prim_ctx.tileoff = 0; + mContext.ctx.prim_ctx.pos = 0; + mContext.ctx.prim_ctx.size = + ((mPosition.h - 1) & 0xfff) << 16 | ((mPosition.w - 1) & 0xfff); + mContext.ctx.prim_ctx.surf = 0; + mContext.ctx.prim_ctx.contalpa = planeAlpha; + mContext.ctx.prim_ctx.cntr = PixelFormat::PLANE_PIXEL_FORMAT_BGRA8888; + mContext.ctx.prim_ctx.cntr |= 0x80000000; + + // turn off premultipled alpha blending for HWC_BLENDING_COVERAGE + if (mBlending == HWC_BLENDING_COVERAGE) { + mContext.ctx.prim_ctx.cntr |= (0x1 << 23); + } + + if (mPanelOrientation == PANEL_ORIENTATION_180) + mContext.ctx.prim_ctx.cntr |= (0x1 << 15); + + VTRACE("type = %d, index = %d, cntr = %#x, linoff = %#x, stride = %#x," + "surf = %#x, pos = %#x, size = %#x, contalpa = %#x", mType, mIndex, + mContext.ctx.prim_ctx.cntr, + mContext.ctx.prim_ctx.linoff, + mContext.ctx.prim_ctx.stride, + mContext.ctx.prim_ctx.surf, + mContext.ctx.prim_ctx.pos, + mContext.ctx.prim_ctx.size, + mContext.ctx.sp_ctx.contalpa); + + mCurrentDataBuffer = handle; +} + +} // namespace intel +} // namespace android diff --git a/merrifield/ips/anniedale/AnnRGBPlane.h b/merrifield/ips/anniedale/AnnRGBPlane.h new file mode 100644 index 0000000..d1a9e60 --- /dev/null +++ b/merrifield/ips/anniedale/AnnRGBPlane.h @@ -0,0 +1,57 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef ANN_RGB_PLANE_H +#define ANN_RGB_PLANE_H + +#include <utils/KeyedVector.h> +#include <hal_public.h> +#include <Hwcomposer.h> +#include <BufferCache.h> +#include <DisplayPlane.h> + +#include <linux/psb_drm.h> + +namespace android { +namespace intel { + +class AnnRGBPlane : public DisplayPlane { +public: + AnnRGBPlane(int index, int type, int disp); + virtual ~AnnRGBPlane(); +public: + // hardware operations + bool enable(); + bool disable(); + bool isDisabled(); + void postFlip(); + + void* getContext() const; + void setZOrderConfig(ZOrderConfig& config, void *nativeConfig); + + bool setDataBuffer(buffer_handle_t handle); +protected: + bool setDataBuffer(BufferMapper& mapper); + bool enablePlane(bool enabled); +private: + void setFramebufferTarget(buffer_handle_t handle); +protected: + struct intel_dc_plane_ctx mContext; +}; + +} // namespace intel +} // namespace android + +#endif /* ANN_RGB_PLANE_H */ diff --git a/merrifield/ips/anniedale/PlaneCapabilities.cpp b/merrifield/ips/anniedale/PlaneCapabilities.cpp new file mode 100644 index 0000000..853511b --- /dev/null +++ b/merrifield/ips/anniedale/PlaneCapabilities.cpp @@ -0,0 +1,280 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <HwcTrace.h> +#include <DisplayPlane.h> +#include <hal_public.h> +#include <OMX_IVCommon.h> +#include <OMX_IntelVideoExt.h> +#include <PlaneCapabilities.h> +#include <common/OverlayHardware.h> +#include <HwcLayer.h> +#include <BufferManager.h> +#include <Hwcomposer.h> + + +#define SPRITE_PLANE_MAX_STRIDE_TILED 16384 +#define SPRITE_PLANE_MAX_STRIDE_LINEAR 16384 + +#define OVERLAY_PLANE_MAX_STRIDE_PACKED 4096 +#define OVERLAY_PLANE_MAX_STRIDE_LINEAR 8192 + +namespace android { +namespace intel { + +bool PlaneCapabilities::isFormatSupported(int planeType, HwcLayer *hwcLayer) +{ + uint32_t format = hwcLayer->getFormat(); + uint32_t trans = hwcLayer->getLayer()->transform; + + if (planeType == DisplayPlane::PLANE_SPRITE || planeType == DisplayPlane::PLANE_PRIMARY) { + switch (format) { + case HAL_PIXEL_FORMAT_BGRA_8888: + case HAL_PIXEL_FORMAT_BGRX_8888: + case HAL_PIXEL_FORMAT_RGBA_8888: + case HAL_PIXEL_FORMAT_RGBX_8888: + case HAL_PIXEL_FORMAT_RGB_565: + return trans ? false : true; + default: + VTRACE("unsupported format %#x", format); + return false; + } + } else if (planeType == DisplayPlane::PLANE_OVERLAY) { + switch (format) { + case HAL_PIXEL_FORMAT_I420: + case HAL_PIXEL_FORMAT_YUY2: + case HAL_PIXEL_FORMAT_UYVY: + // TODO: overlay supports 180 degree rotation + if (trans == HAL_TRANSFORM_ROT_180) { + WTRACE("180 degree rotation is not supported yet"); + } + return trans ? false : true; + case HAL_PIXEL_FORMAT_YV12: + case HAL_PIXEL_FORMAT_NV12: + case OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar: + case OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar_Tiled: + return true; + default: + VTRACE("unsupported format %#x", format); + return false; + } + } else { + ETRACE("invalid plane type %d", planeType); + return false; + } +} + +bool PlaneCapabilities::isSizeSupported(int planeType, HwcLayer *hwcLayer) +{ + uint32_t format = hwcLayer->getFormat(); + uint32_t w = hwcLayer->getBufferWidth(); + uint32_t h = hwcLayer->getBufferHeight(); + const stride_t& stride = hwcLayer->getBufferStride(); + + bool isYUVPacked; + uint32_t maxStride; + + if (planeType == DisplayPlane::PLANE_SPRITE || planeType == DisplayPlane::PLANE_PRIMARY) { + switch (format) { + case HAL_PIXEL_FORMAT_BGRA_8888: + case HAL_PIXEL_FORMAT_BGRX_8888: + case HAL_PIXEL_FORMAT_RGBA_8888: + case HAL_PIXEL_FORMAT_RGBX_8888: + case HAL_PIXEL_FORMAT_RGB_565: + VTRACE("stride %d", stride.rgb.stride); + if (stride.rgb.stride > SPRITE_PLANE_MAX_STRIDE_LINEAR) { + VTRACE("too large stride %d", stride.rgb.stride); + return false; + } + return true; + default: + VTRACE("unsupported format %#x", format); + return false; + } + } else if (planeType == DisplayPlane::PLANE_OVERLAY) { + switch (format) { + case HAL_PIXEL_FORMAT_YV12: + case HAL_PIXEL_FORMAT_I420: + case HAL_PIXEL_FORMAT_NV12: + case OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar: + case OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar_Tiled: + isYUVPacked = false; + break; + case HAL_PIXEL_FORMAT_YUY2: + case HAL_PIXEL_FORMAT_UYVY: + isYUVPacked = true; + break; + default: + VTRACE("unsupported format %#x", format); + return false; + } + // don't use overlay plane if stride is too big + maxStride = OVERLAY_PLANE_MAX_STRIDE_LINEAR; + if (isYUVPacked) { + maxStride = OVERLAY_PLANE_MAX_STRIDE_PACKED; + } + + if (stride.yuv.yStride > maxStride) { + VTRACE("stride %d is too large", stride.yuv.yStride); + return false; + } + + hwc_frect_t& srcCrop = hwcLayer->getLayer()->sourceCropf; + uint32_t width = srcCrop.right - srcCrop.left; + uint32_t height = srcCrop.bottom - srcCrop.top; + + if (width <= 64 || height <= 64) { + DTRACE("width or height of source crop is less than 64, fallback to GLES"); + return false; + } + + if ((height & 0x1) || (width & 0x1)){ + if (!hwcLayer->isProtected()) { + DTRACE("unprotected video content, height or width of source crop is not even, fallback to GLES "); + return false; + } + } + + return true; + } else { + ETRACE("invalid plane type %d", planeType); + return false; + } +} + +bool PlaneCapabilities::isBlendingSupported(int planeType, HwcLayer *hwcLayer) +{ + uint32_t blending = (uint32_t)hwcLayer->getLayer()->blending; + uint8_t planeAlpha = hwcLayer->getLayer()->planeAlpha; + + if (planeType == DisplayPlane::PLANE_SPRITE || planeType == DisplayPlane::PLANE_PRIMARY) { + // support premultipled & none blanding + switch (blending) { + case HWC_BLENDING_NONE: + case HWC_BLENDING_PREMULT: + // add coverage alpha support for ann + case HWC_BLENDING_COVERAGE: + return true; + default: + VTRACE("unsupported blending %#x", blending); + return false; + } + } else if (planeType == DisplayPlane::PLANE_OVERLAY) { + // overlay doesn't support blending + return (blending == HWC_BLENDING_NONE) ? true : false; + } else { + ETRACE("invalid plane type %d", planeType); + return false; + } +} + +bool PlaneCapabilities::isScalingSupported(int planeType, HwcLayer *hwcLayer) +{ + hwc_frect_t& src = hwcLayer->getLayer()->sourceCropf; + hwc_rect_t& dest = hwcLayer->getLayer()->displayFrame; + uint32_t trans = hwcLayer->getLayer()->transform; + + int srcW, srcH; + int dstW, dstH; + + srcW = (int)src.right - (int)src.left; + srcH = (int)src.bottom - (int)src.top; + dstW = dest.right - dest.left; + dstH = dest.bottom - dest.top; + + if (planeType == DisplayPlane::PLANE_SPRITE || planeType == DisplayPlane::PLANE_PRIMARY) { + // no scaling is supported + return ((srcW == dstW) && (srcH == dstH)) ? true : false; + + } else if (planeType == DisplayPlane::PLANE_OVERLAY) { + // overlay cannot support resolution that bigger than 2047x2047. + if ((srcW > INTEL_OVERLAY_MAX_WIDTH - 1) || (srcH > INTEL_OVERLAY_MAX_HEIGHT - 1)) { + uint32_t format = hwcLayer->getFormat(); + if (format == OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar || + format == OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar_Tiled) { + // will fall back to GLES if no scaling buffer provided by ved later + // so don't return false and print a warning, it's for video format only. + WTRACE("source size %dx%d hit overlay resolution limitation.", srcW, srcH); + } else { + return false; + } + } + + if (dstW <= 100 || dstH <= 1 || srcW <= 100 || srcH <= 1) { + // Workaround: Overlay flip when height is 1 causes MIPI stall on TNG + DTRACE("invalid destination size: %dx%d, fall back to GLES", dstW, dstH); + return false; + } + + if (trans == HAL_TRANSFORM_ROT_90 || trans == HAL_TRANSFORM_ROT_270) { + int tmp = srcW; + srcW = srcH; + srcH = tmp; + } + + if (!hwcLayer->isProtected()) { + if ((int)src.left & 63) { + DTRACE("offset %d is not 64 bytes aligned, fall back to GLES", (int)src.left); + return false; + } + + float scaleX = (float)srcW / dstW; + float scaleY = (float)srcH / dstH; + if (scaleX >= 3 || scaleY >= 3) { + DTRACE("overlay rotation with scaling >= 3, fall back to GLES"); + return false; + } +#if 0 + if (trans == HAL_TRANSFORM_ROT_90 && (float)srcW / srcH != (float)dstW / dstH) { + // FIXME: work aournd for pipe crashing issue, when rotate screen + // from 90 to 0 degree (with Sharp 25x16 panel). + DTRACE("overlay rotation with uneven scaling, fall back to GLES"); + return false; + } +#endif + } + + return true; + } else { + ETRACE("invalid plane type %d", planeType); + return false; + } +} + +bool PlaneCapabilities::isTransformSupported(int planeType, HwcLayer *hwcLayer) +{ + uint32_t trans = hwcLayer->getLayer()->transform; + + if (planeType == DisplayPlane::PLANE_OVERLAY) { + // overlay does not support FLIP_H/FLIP_V + switch (trans) { + case 0: + case HAL_TRANSFORM_ROT_90: + case HAL_TRANSFORM_ROT_180: + case HAL_TRANSFORM_ROT_270: + return true; + default: + return false; + } + } + + // don't transform any tranform + return trans ? false : true; +} + +} // namespace intel +} // namespace android + diff --git a/merrifield/ips/common/BlankControl.cpp b/merrifield/ips/common/BlankControl.cpp new file mode 100644 index 0000000..3ac0736 --- /dev/null +++ b/merrifield/ips/common/BlankControl.cpp @@ -0,0 +1,43 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <HwcTrace.h> +#include <Drm.h> +#include <common/BlankControl.h> +#include <Hwcomposer.h> + +namespace android { +namespace intel { + +BlankControl::BlankControl() + : IBlankControl() +{ +} + +BlankControl::~BlankControl() +{ +} + +bool BlankControl::blank(int disp, bool blank) +{ + // current do nothing but return true + // use PM to trigger screen blank/unblank + VTRACE("blank is not supported yet, disp %d, blank %d", disp, blank); + return true; +} + +} // namespace intel +} // namespace android diff --git a/merrifield/ips/common/BlankControl.h b/merrifield/ips/common/BlankControl.h new file mode 100644 index 0000000..1c0de05 --- /dev/null +++ b/merrifield/ips/common/BlankControl.h @@ -0,0 +1,36 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef BLANK_CONTROL_H +#define BLANK_CONTROL_H + +#include <IBlankControl.h> + +namespace android { +namespace intel { + +class BlankControl : public IBlankControl { +public: + BlankControl(); + virtual ~BlankControl(); + +public: + bool blank(int disp, bool blank); +}; + +} // namespace intel +} // namespace android + +#endif /* BLANK_CONTROL_H */ diff --git a/merrifield/ips/common/DrmConfig.cpp b/merrifield/ips/common/DrmConfig.cpp new file mode 100644 index 0000000..59b1c72 --- /dev/null +++ b/merrifield/ips/common/DrmConfig.cpp @@ -0,0 +1,90 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <HwcTrace.h> +#include <IDisplayDevice.h> +#include <Drm.h> +#include <DrmConfig.h> + + +namespace android { +namespace intel { + +const char* DrmConfig::getDrmPath() +{ + return "/dev/card0"; +} + +uint32_t DrmConfig::getDrmConnector(int device) +{ + if (device == IDisplayDevice::DEVICE_PRIMARY) + return DRM_MODE_CONNECTOR_MIPI; + else if (device == IDisplayDevice::DEVICE_EXTERNAL) + return DRM_MODE_CONNECTOR_DVID; + return DRM_MODE_CONNECTOR_Unknown; +} + +uint32_t DrmConfig::getDrmEncoder(int device) +{ + if (device == IDisplayDevice::DEVICE_PRIMARY) + return DRM_MODE_ENCODER_MIPI; + else if (device == IDisplayDevice::DEVICE_EXTERNAL) + return DRM_MODE_ENCODER_TMDS; + return DRM_MODE_ENCODER_NONE; +} + +uint32_t DrmConfig::getFrameBufferFormat() +{ + return HAL_PIXEL_FORMAT_RGBX_8888; +} + +uint32_t DrmConfig::getFrameBufferDepth() +{ + return 24; +} + +uint32_t DrmConfig::getFrameBufferBpp() +{ + return 32; +} + +const char* DrmConfig::getUeventEnvelope() +{ + return "change@/devices/pci0000:00/0000:00:02.0/drm/card0"; +} + +const char* DrmConfig::getHotplugString() +{ + return "HOTPLUG=1"; +} + +const char* DrmConfig::getRepeatedFrameString() +{ + return "REPEATED_FRAME"; +} + +uint32_t DrmConfig::convertHalFormatToDrmFormat(uint32_t halFormat) +{ + switch (halFormat) { + case HAL_PIXEL_FORMAT_RGBX_8888: + return DRM_FORMAT_XRGB8888; + default: + ETRACE("format %#x isn't supported by drm", halFormat); + return 0; + } +} + +} // namespace intel +} // namespace android diff --git a/merrifield/ips/common/DrmControl.cpp b/merrifield/ips/common/DrmControl.cpp new file mode 100644 index 0000000..9c34658 --- /dev/null +++ b/merrifield/ips/common/DrmControl.cpp @@ -0,0 +1,58 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <HwcTrace.h> +#include <linux/psb_drm.h> +#include <Hwcomposer.h> +#include <common/DrmControl.h> + +namespace android { +namespace intel { + +DrmControl::DrmControl() + : mVideoExtCommand(0) +{ +} + +DrmControl::~DrmControl() +{ +} + +int DrmControl::getVideoExtCommand() +{ + if (mVideoExtCommand) { + return mVideoExtCommand; + } + + int fd = Hwcomposer::getInstance().getDrm()->getDrmFd(); + + union drm_psb_extension_arg video_getparam_arg; + strncpy(video_getparam_arg.extension, + "lnc_video_getparam", sizeof(video_getparam_arg.extension)); + int ret = drmCommandWriteRead(fd, DRM_PSB_EXTENSION, + &video_getparam_arg, sizeof(video_getparam_arg)); + if (ret != 0) { + VTRACE("failed to get video extension command"); + return 0; + } + + mVideoExtCommand = video_getparam_arg.rep.driver_ioctl_offset; + + return mVideoExtCommand; +} + +} // namespace intel +} // namespace android diff --git a/merrifield/ips/common/DrmControl.h b/merrifield/ips/common/DrmControl.h new file mode 100644 index 0000000..fbd0284 --- /dev/null +++ b/merrifield/ips/common/DrmControl.h @@ -0,0 +1,38 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef DRM_CONTROL_H +#define DRM_CONTROL_H + + +namespace android { +namespace intel { + +class DrmControl { +public: + DrmControl(); + virtual ~DrmControl(); + +public: + int getVideoExtCommand(); + +private: + int mVideoExtCommand; +}; + +} // namespace intel +} // namespace android + +#endif /* DRM_CONTROL_H */ diff --git a/merrifield/ips/common/GrallocBufferBase.cpp b/merrifield/ips/common/GrallocBufferBase.cpp new file mode 100644 index 0000000..e50b777 --- /dev/null +++ b/merrifield/ips/common/GrallocBufferBase.cpp @@ -0,0 +1,91 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <HwcTrace.h> +#include <common/GrallocBufferBase.h> +#include <DisplayQuery.h> + + +namespace android { +namespace intel { + +GrallocBufferBase::GrallocBufferBase(buffer_handle_t handle) + : GraphicBuffer(handle) +{ + ATRACE("handle = %#x", handle); + initBuffer(handle); +} + +void GrallocBufferBase::resetBuffer(buffer_handle_t handle) +{ + GraphicBuffer::resetBuffer(handle); + initBuffer(handle); +} + +void GrallocBufferBase::initBuffer(buffer_handle_t handle) +{ + // nothing to initialize +} + +void GrallocBufferBase::initStride() +{ + int yStride, uvStride; + + // setup stride + switch (mFormat) { + case HAL_PIXEL_FORMAT_YV12: + case HAL_PIXEL_FORMAT_I420: + uint32_t yStride_align; + yStride_align = DisplayQuery::getOverlayLumaStrideAlignment(mFormat); + if (yStride_align > 0) + { + yStride = align_to(align_to(mWidth, 32), yStride_align); + } + else + { + yStride = align_to(align_to(mWidth, 32), 64); + } + uvStride = align_to(yStride >> 1, 64); + mStride.yuv.yStride = yStride; + mStride.yuv.uvStride = uvStride; + break; + case HAL_PIXEL_FORMAT_NV12: + yStride = align_to(align_to(mWidth, 32), 64); + uvStride = yStride; + mStride.yuv.yStride = yStride; + mStride.yuv.uvStride = uvStride; + break; + case OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar: + case OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar_Tiled: + yStride = align_to(align_to(mWidth, 32), 64); + uvStride = yStride; + mStride.yuv.yStride = yStride; + mStride.yuv.uvStride = uvStride; + break; + case HAL_PIXEL_FORMAT_YUY2: + case HAL_PIXEL_FORMAT_UYVY: + yStride = align_to((align_to(mWidth, 32) << 1), 64); + uvStride = 0; + mStride.yuv.yStride = yStride; + mStride.yuv.uvStride = uvStride; + break; + default: + mStride.rgb.stride = align_to(((mBpp >> 3) * align_to(mWidth, 32)), 64); + break; + } +} + +} +} diff --git a/merrifield/ips/common/GrallocBufferBase.h b/merrifield/ips/common/GrallocBufferBase.h new file mode 100644 index 0000000..5ae8c95 --- /dev/null +++ b/merrifield/ips/common/GrallocBufferBase.h @@ -0,0 +1,45 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef GRALLOC_BUFFER_BASE_H +#define GRALLOC_BUFFER_BASE_H + +#include <GraphicBuffer.h> +#include <hal_public.h> +// FIXME: remove it, why define NV12_VED based on OMX's value? +#include <OMX_IVCommon.h> +#include <OMX_IntelVideoExt.h> + +namespace android { +namespace intel { + +class GrallocBufferBase : public GraphicBuffer { +public: + GrallocBufferBase(buffer_handle_t handle); + virtual ~GrallocBufferBase() {} + virtual void resetBuffer(buffer_handle_t handle); + +protected: + // helper function to be invoked by the derived class + void initStride(); + +private: + void initBuffer(buffer_handle_t handle); +}; + +} // namespace intel +} // namespace android + +#endif /* GRALLOC_BUFFER_BASE_H */ diff --git a/merrifield/ips/common/GrallocBufferMapperBase.cpp b/merrifield/ips/common/GrallocBufferMapperBase.cpp new file mode 100644 index 0000000..ac236e3 --- /dev/null +++ b/merrifield/ips/common/GrallocBufferMapperBase.cpp @@ -0,0 +1,72 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <HwcTrace.h> +#include <Drm.h> +#include <Hwcomposer.h> +#include <common/GrallocBufferMapperBase.h> + +namespace android { +namespace intel { + +GrallocBufferMapperBase::GrallocBufferMapperBase(DataBuffer& buffer) + : BufferMapper(buffer) +{ + CTRACE(); + + for (int i = 0; i < SUB_BUFFER_MAX; i++) { + mGttOffsetInPage[i] = 0; + mCpuAddress[i] = 0; + mSize[i] = 0; + mKHandle[i] = 0; + } +} + +GrallocBufferMapperBase::~GrallocBufferMapperBase() +{ + CTRACE(); +} + +uint32_t GrallocBufferMapperBase::getGttOffsetInPage(int subIndex) const +{ + if (subIndex >= 0 && subIndex < SUB_BUFFER_MAX) + return mGttOffsetInPage[subIndex]; + return 0; +} + +void* GrallocBufferMapperBase::getCpuAddress(int subIndex) const +{ + if (subIndex >=0 && subIndex < SUB_BUFFER_MAX) + return mCpuAddress[subIndex]; + return 0; +} + +uint32_t GrallocBufferMapperBase::getSize(int subIndex) const +{ + if (subIndex >= 0 && subIndex < SUB_BUFFER_MAX) + return mSize[subIndex]; + return 0; +} + +buffer_handle_t GrallocBufferMapperBase::getKHandle(int subIndex) +{ + if (subIndex >= 0 && subIndex < SUB_BUFFER_MAX) + return mKHandle[subIndex]; + return 0; +} + + +} // namespace intel +} // namespace android diff --git a/merrifield/ips/common/GrallocBufferMapperBase.h b/merrifield/ips/common/GrallocBufferMapperBase.h new file mode 100644 index 0000000..fed0d8e --- /dev/null +++ b/merrifield/ips/common/GrallocBufferMapperBase.h @@ -0,0 +1,52 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef GRALLOC_BUFFER_MAPPER_BASE_H +#define GRALLOC_BUFFER_MAPPER_BASE_H + +#include <BufferMapper.h> +#include <common/GrallocSubBuffer.h> +#include <common/GrallocBufferBase.h> + +namespace android { +namespace intel { + +class GrallocBufferMapperBase : public BufferMapper { +public: + GrallocBufferMapperBase(DataBuffer& buffer); + virtual ~GrallocBufferMapperBase(); +public: + virtual bool map() = 0; + virtual bool unmap() = 0; + + uint32_t getGttOffsetInPage(int subIndex) const; + void* getCpuAddress(int subIndex) const; + uint32_t getSize(int subIndex) const; + virtual buffer_handle_t getKHandle(int subIndex); + virtual buffer_handle_t getFbHandle(int subIndex) = 0; + virtual void putFbHandle() = 0; + +protected: + // mapped info + uint32_t mGttOffsetInPage[SUB_BUFFER_MAX]; + void* mCpuAddress[SUB_BUFFER_MAX]; + uint32_t mSize[SUB_BUFFER_MAX]; + buffer_handle_t mKHandle[SUB_BUFFER_MAX]; +}; + +} // namespace intel +} // namespace android + +#endif /* TNG_GRALLOC_BUFFER_MAPPER_H */ diff --git a/merrifield/ips/common/GrallocSubBuffer.h b/merrifield/ips/common/GrallocSubBuffer.h new file mode 100644 index 0000000..15170f2 --- /dev/null +++ b/merrifield/ips/common/GrallocSubBuffer.h @@ -0,0 +1,35 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef GRALLOC_SUB_BUFFER_H +#define GRALLOC_SUB_BUFFER_H + + +namespace android { +namespace intel { + +enum { + SUB_BUFFER0 = 0, + SUB_BUFFER1, + SUB_BUFFER2, + SUB_BUFFER_MAX, +}; + +} // namespace intel +} // namespace android + + +#endif /* GRALLOC_SUB_BUFFER_H */ + diff --git a/merrifield/ips/common/HdcpControl.cpp b/merrifield/ips/common/HdcpControl.cpp new file mode 100644 index 0000000..620d28f --- /dev/null +++ b/merrifield/ips/common/HdcpControl.cpp @@ -0,0 +1,380 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <HwcTrace.h> +#include <DrmConfig.h> +#include <Hwcomposer.h> +#include <DisplayQuery.h> +#include <common/DrmControl.h> +#include <common/HdcpControl.h> +#include <cutils/properties.h> + + +namespace android { +namespace intel { + +HdcpControl::HdcpControl() + : mCallback(NULL), + mUserData(NULL), + mCallbackState(CALLBACK_PENDING), + mMutex(), + mStoppedCondition(), + mCompletedCondition(), + mWaitForCompletion(false), + mStopped(true), + mAuthenticated(false), + mActionDelay(0), + mAuthRetryCount(0) +{ +} + +HdcpControl::~HdcpControl() +{ +} + +bool HdcpControl::startHdcp() +{ + // this is a blocking and synchronous call + Mutex::Autolock lock(mMutex); + + char prop[PROPERTY_VALUE_MAX]; + if (property_get("debug.hwc.hdcp.enable", prop, "1") > 0) { + if (atoi(prop) == 0) { + WTRACE("HDCP is disabled"); + return false; + } + } + + if (!mStopped) { + WTRACE("HDCP has been started"); + return true; + } + + mStopped = false; + mAuthenticated = false; + mWaitForCompletion = false; + + mThread = new HdcpControlThread(this); + if (!mThread.get()) { + ETRACE("failed to create hdcp control thread"); + return false; + } + + if (!runHdcp()) { + ETRACE("failed to run HDCP"); + mStopped = true; + mThread = NULL; + return false; + } + + mAuthRetryCount = 0; + mWaitForCompletion = !mAuthenticated; + if (mAuthenticated) { + mActionDelay = HDCP_VERIFICATION_DELAY_MS; + } else { + mActionDelay = HDCP_AUTHENTICATION_SHORT_DELAY_MS; + } + + mThread->run("HdcpControl", PRIORITY_NORMAL); + + if (!mWaitForCompletion) { + // HDCP is authenticated. + return true; + } + status_t err = mCompletedCondition.waitRelative(mMutex, milliseconds(HDCP_AUTHENTICATION_TIMEOUT_MS)); + if (err == -ETIMEDOUT) { + WTRACE("timeout waiting for completion"); + } + mWaitForCompletion = false; + return mAuthenticated; +} + +bool HdcpControl::startHdcpAsync(HdcpStatusCallback cb, void *userData) +{ + char prop[PROPERTY_VALUE_MAX]; + if (property_get("debug.hwc.hdcp.enable", prop, "1") > 0) { + if (atoi(prop) == 0) { + WTRACE("HDCP is disabled"); + return false; + } + } + + if (cb == NULL || userData == NULL) { + ETRACE("invalid callback or user data"); + return false; + } + + Mutex::Autolock lock(mMutex); + + if (!mStopped) { + WTRACE("HDCP has been started"); + return true; + } + + mThread = new HdcpControlThread(this); + if (!mThread.get()) { + ETRACE("failed to create hdcp control thread"); + return false; + } + + mAuthRetryCount = 0; + mCallback = cb; + mUserData = userData; + mCallbackState = CALLBACK_PENDING; + mWaitForCompletion = false; + mAuthenticated = false; + mStopped = false; + mActionDelay = HDCP_ASYNC_START_DELAY_MS; + mThread->run("HdcpControl", PRIORITY_NORMAL); + + return true; +} + +bool HdcpControl::stopHdcp() +{ + do { + Mutex::Autolock lock(mMutex); + if (mStopped) { + return true; + } + + mStopped = true; + mStoppedCondition.signal(); + + mAuthenticated = false; + mWaitForCompletion = false; + mCallback = NULL; + mUserData = NULL; + disableAuthentication(); + } while (0); + + if (mThread.get()) { + mThread->requestExitAndWait(); + mThread = NULL; + } + + return true; +} + +bool HdcpControl::enableAuthentication() +{ + int fd = Hwcomposer::getInstance().getDrm()->getDrmFd(); + int ret = drmCommandNone(fd, DRM_PSB_ENABLE_HDCP); + if (ret != 0) { + ETRACE("failed to enable HDCP authentication"); + return false; + } + return true; +} + +bool HdcpControl::disableAuthentication() +{ + int fd = Hwcomposer::getInstance().getDrm()->getDrmFd(); + int ret = drmCommandNone(fd, DRM_PSB_DISABLE_HDCP); + if (ret != 0) { + ETRACE("failed to stop disable authentication"); + return false; + } + return true; +} + +bool HdcpControl::enableOverlay() +{ + return true; +} + +bool HdcpControl::disableOverlay() +{ + return true; +} + +bool HdcpControl::enableDisplayIED() +{ + int fd = Hwcomposer::getInstance().getDrm()->getDrmFd(); + int ret = drmCommandNone(fd, DRM_PSB_HDCP_DISPLAY_IED_ON); + if (ret != 0) { + ETRACE("failed to enable overlay IED"); + return false; + } + return true; +} + +bool HdcpControl::disableDisplayIED() +{ + int fd = Hwcomposer::getInstance().getDrm()->getDrmFd(); + int ret = drmCommandNone(fd, DRM_PSB_HDCP_DISPLAY_IED_OFF); + if (ret != 0) { + ETRACE("failed to disable overlay IED"); + return false; + } + return true; +} + +bool HdcpControl::isHdcpSupported() +{ + int fd = Hwcomposer::getInstance().getDrm()->getDrmFd(); + unsigned int caps = 0; + int ret = drmCommandRead(fd, DRM_PSB_QUERY_HDCP, &caps, sizeof(caps)); + if (ret != 0) { + ETRACE("failed to query HDCP capability"); + return false; + } + if (caps == 0) { + WTRACE("HDCP is not supported"); + return false; + } else { + ITRACE("HDCP is supported"); + return true; + } +} + +bool HdcpControl::checkAuthenticated() +{ + int fd = Hwcomposer::getInstance().getDrm()->getDrmFd(); + unsigned int match = 0; + int ret = drmCommandRead(fd, DRM_PSB_GET_HDCP_LINK_STATUS, &match, sizeof(match)); + if (ret != 0) { + ETRACE("failed to get hdcp link status"); + return false; + } + if (match) { + VTRACE("HDCP is authenticated"); + mAuthenticated = true; + } else { + ETRACE("HDCP is not authenticated"); + mAuthenticated = false; + } + return mAuthenticated; +} + +bool HdcpControl::runHdcp() +{ + // Default return value is true so HDCP can be re-authenticated in the working thread + bool ret = true; + + preRunHdcp(); + + for (int i = 0; i < HDCP_INLOOP_RETRY_NUMBER; i++) { + VTRACE("enable and verify HDCP, iteration# %d", i); + if (mStopped) { + WTRACE("HDCP authentication has been stopped"); + ret = false; + break; + } + + if (!enableAuthentication()) { + ETRACE("HDCP authentication failed. Retry"); + mAuthenticated = false; + ret = true; + } else { + ITRACE("HDCP is authenticated"); + mAuthenticated = true; + ret = true; + break; + } + + if (mStopped) { + WTRACE("HDCP authentication has been stopped"); + ret = false; + break; + } + + if (i < HDCP_INLOOP_RETRY_NUMBER - 1) { + // Adding delay to make sure panel receives video signal so it can start HDCP authentication. + // (HDCP spec 1.3, section 2.3) + usleep(HDCP_INLOOP_RETRY_DELAY_US); + } + } + + postRunHdcp(); + + return ret; +} + +bool HdcpControl::preRunHdcp() +{ + // TODO: for CTP platform, IED needs to be disabled during HDCP authentication. + return true; +} + +bool HdcpControl::postRunHdcp() +{ + // TODO: for CTP platform, IED needs to be disabled during HDCP authentication. + return true; +} + + +void HdcpControl::signalCompletion() +{ + if (mWaitForCompletion) { + ITRACE("signal HDCP authentication completed, status = %d", mAuthenticated); + mCompletedCondition.signal(); + mWaitForCompletion = false; + } +} + +bool HdcpControl::threadLoop() +{ + Mutex::Autolock lock(mMutex); + status_t err = mStoppedCondition.waitRelative(mMutex, milliseconds(mActionDelay)); + if (err != -ETIMEDOUT) { + ITRACE("Hdcp is stopped."); + signalCompletion(); + return false; + } + + // default is to keep thread active + bool ret = true; + if (!mAuthenticated) { + ret = runHdcp(); + mAuthRetryCount++; + } else { + mAuthRetryCount = 0; + checkAuthenticated(); + } + + // set next action delay + if (mAuthenticated) { + mActionDelay = HDCP_VERIFICATION_DELAY_MS; + } else { + // If HDCP can not authenticate after "HDCP_RETRY_LIMIT" attempts + // reduce HDCP retry frequency to 2 sec + if (mAuthRetryCount >= HDCP_RETRY_LIMIT) { + mActionDelay = HDCP_AUTHENTICATION_LONG_DELAY_MS; + } else { + mActionDelay = HDCP_AUTHENTICATION_SHORT_DELAY_MS; + } + } + + // TODO: move out of lock? + if (!ret || mAuthenticated) { + signalCompletion(); + } + + if (mCallback) { + if ((mAuthenticated && mCallbackState == CALLBACK_AUTHENTICATED) || + (!mAuthenticated && mCallbackState == CALLBACK_NOT_AUTHENTICATED)) { + // ignore callback as state is not changed + } else { + mCallbackState = + mAuthenticated ? CALLBACK_AUTHENTICATED : CALLBACK_NOT_AUTHENTICATED; + (*mCallback)(mAuthenticated, mUserData); + } + } + return ret; +} + +} // namespace intel +} // namespace android diff --git a/merrifield/ips/common/HdcpControl.h b/merrifield/ips/common/HdcpControl.h new file mode 100644 index 0000000..5dc33c9 --- /dev/null +++ b/merrifield/ips/common/HdcpControl.h @@ -0,0 +1,87 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef HDCP_CONTROL_H +#define HDCP_CONTROL_H + +#include <IHdcpControl.h> +#include <SimpleThread.h> + +namespace android { +namespace intel { + +class HdcpControl : public IHdcpControl { +public: + HdcpControl(); + virtual ~HdcpControl(); + +public: + virtual bool startHdcp(); + virtual bool startHdcpAsync(HdcpStatusCallback cb, void *userData); + virtual bool stopHdcp(); + +protected: + bool enableAuthentication(); + bool disableAuthentication(); + bool enableOverlay(); + bool disableOverlay(); + bool enableDisplayIED(); + bool disableDisplayIED(); + bool isHdcpSupported(); + bool checkAuthenticated(); + virtual bool preRunHdcp(); + virtual bool postRunHdcp(); + bool runHdcp(); + inline void signalCompletion(); + +private: + enum { + HDCP_INLOOP_RETRY_NUMBER = 1, + HDCP_INLOOP_RETRY_DELAY_US = 50000, + HDCP_VERIFICATION_DELAY_MS = 2000, + HDCP_ASYNC_START_DELAY_MS = 100, + HDCP_AUTHENTICATION_SHORT_DELAY_MS = 200, + HDCP_AUTHENTICATION_LONG_DELAY_MS = 2000, + HDCP_AUTHENTICATION_TIMEOUT_MS = 5000, + HDCP_RETRY_LIMIT = 10, + }; + + enum { + CALLBACK_PENDING, + CALLBACK_AUTHENTICATED, + CALLBACK_NOT_AUTHENTICATED, + }; + +protected: + HdcpStatusCallback mCallback; + void *mUserData; + int mCallbackState; + Mutex mMutex; + Condition mStoppedCondition; + Condition mCompletedCondition; + bool mWaitForCompletion; + bool mStopped; + bool mAuthenticated; + int mActionDelay; // in milliseconds + uint32_t mAuthRetryCount; + +private: + DECLARE_THREAD(HdcpControlThread, HdcpControl); +}; + +} // namespace intel +} // namespace android + +#endif /* HDCP_CONTROL_H */ diff --git a/merrifield/ips/common/OverlayHardware.h b/merrifield/ips/common/OverlayHardware.h new file mode 100644 index 0000000..a06f304 --- /dev/null +++ b/merrifield/ips/common/OverlayHardware.h @@ -0,0 +1,160 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef OVERLAY_HARDWARE_H +#define OVERLAY_HARDWARE_H + +namespace android { +namespace intel { + +// only one overlay data buffer for testing +#define INTEL_OVERLAY_BUFFER_NUM 1 +#define INTEL_OVERLAY_MAX_WIDTH 2048 +#define INTEL_OVERLAY_MAX_HEIGHT 2048 +#define INTEL_OVERLAY_MIN_STRIDE 512 +#define INTEL_OVERLAY_MAX_STRIDE_PACKED (8 * 1024) +#define INTEL_OVERLAY_MAX_STRIDE_LINEAR (4 * 1024) +#define INTEL_OVERLAY_MAX_SCALING_RATIO 7 + +// Polyphase filter coefficients +#define N_HORIZ_Y_TAPS 5 +#define N_VERT_Y_TAPS 3 +#define N_HORIZ_UV_TAPS 3 +#define N_VERT_UV_TAPS 3 +#define N_PHASES 17 +#define MAX_TAPS 5 + +// Filter cutoff frequency limits. +#define MIN_CUTOFF_FREQ 1.0 +#define MAX_CUTOFF_FREQ 3.0 + +// Overlay init micros +#define OVERLAY_INIT_CONTRAST 0x4b +#define OVERLAY_INIT_BRIGHTNESS -19 +#define OVERLAY_INIT_SATURATION 0x92 +#define OVERLAY_INIT_GAMMA0 0x080808 +#define OVERLAY_INIT_GAMMA1 0x101010 +#define OVERLAY_INIT_GAMMA2 0x202020 +#define OVERLAY_INIT_GAMMA3 0x404040 +#define OVERLAY_INIT_GAMMA4 0x808080 +#define OVERLAY_INIT_GAMMA5 0xc0c0c0 +#define OVERLAY_INIT_COLORKEY 0 +#define OVERLAY_INIT_COLORKEYMASK ((0x0 << 31) | (0X0 << 30)) +#define OVERLAY_INIT_CONFIG ((0x1 << 18) | (0x1 << 3)) + +// overlay register values +#define OVERLAY_FORMAT_MASK (0xf << 10) +#define OVERLAY_FORMAT_PACKED_YUV422 (0x8 << 10) +#define OVERLAY_FORMAT_PLANAR_NV12_1 (0x7 << 10) +#define OVERLAY_FORMAT_PLANAR_NV12_2 (0xb << 10) +#define OVERLAY_FORMAT_PLANAR_YUV420 (0xc << 10) +#define OVERLAY_FORMAT_PLANAR_YUV422 (0xd << 10) +#define OVERLAY_FORMAT_PLANAR_YUV41X (0xe << 10) + +#define OVERLAY_PACKED_ORDER_YUY2 (0x0 << 14) +#define OVERLAY_PACKED_ORDER_YVYU (0x1 << 14) +#define OVERLAY_PACKED_ORDER_UYVY (0x2 << 14) +#define OVERLAY_PACKED_ORDER_VYUY (0x3 << 14) +#define OVERLAY_PACKED_ORDER_MASK (0x3 << 14) + +#define OVERLAY_MEMORY_LAYOUT_TILED (0x1 << 19) +#define OVERLAY_MEMORY_LAYOUT_LINEAR (0x0 << 19) + +#define OVERLAY_MIRRORING_NORMAL (0x0 << 17) +#define OVERLAY_MIRRORING_HORIZONTAL (0x1 << 17) +#define OVERLAY_MIRRORING_VERTIACAL (0x2 << 17) +#define OVERLAY_MIRRORING_BOTH (0x3 << 17) + +#define BUF_TYPE (0x1<<5) +#define BUF_TYPE_FRAME (0x0<<5) +#define BUF_TYPE_FIELD (0x1<<5) +#define TEST_MODE (0x1<<4) +#define BUFFER_SELECT (0x3<<2) +#define BUFFER0 (0x0<<2) +#define BUFFER1 (0x1<<2) +#define FIELD_SELECT (0x1<<1) +#define FIELD0 (0x0<<1) +#define FIELD1 (0x1<<1) +#define OVERLAY_ENABLE 0x1 + + +// Overlay contorl registers +typedef struct { + uint32_t OBUF_0Y; + uint32_t OBUF_1Y; + uint32_t OBUF_0U; + uint32_t OBUF_0V; + uint32_t OBUF_1U; + uint32_t OBUF_1V; + uint32_t OSTRIDE; + uint32_t YRGB_VPH; + uint32_t UV_VPH; + uint32_t HORZ_PH; + uint32_t INIT_PHS; + uint32_t DWINPOS; + uint32_t DWINSZ; + uint32_t SWIDTH; + uint32_t SWIDTHSW; + uint32_t SHEIGHT; + uint32_t YRGBSCALE; + uint32_t UVSCALE; + uint32_t OCLRC0; + uint32_t OCLRC1; + uint32_t DCLRKV; + uint32_t DCLRKM; + uint32_t SCHRKVH; + uint32_t SCHRKVL; + uint32_t SCHRKEN; + uint32_t OCONFIG; + uint32_t OCMD; + uint32_t RESERVED1; + uint32_t OSTART_0Y; + uint32_t OSTART_1Y; + uint32_t OSTART_0U; + uint32_t OSTART_0V; + uint32_t OSTART_1U; + uint32_t OSTART_1V; + uint32_t OTILEOFF_0Y; + uint32_t OTILEOFF_1Y; + uint32_t OTILEOFF_0U; + uint32_t OTILEOFF_0V; + uint32_t OTILEOFF_1U; + uint32_t OTILEOFF_1V; + uint32_t FASTHSCALE; + uint32_t UVSCALEV; + + uint32_t RESERVEDC[(0x200 - 0xA8) / 4]; + uint16_t Y_VCOEFS[N_VERT_Y_TAPS * N_PHASES]; + uint16_t RESERVEDD[0x100 / 2 - N_VERT_Y_TAPS * N_PHASES]; + uint16_t Y_HCOEFS[N_HORIZ_Y_TAPS * N_PHASES]; + uint16_t RESERVEDE[0x200 / 2 - N_HORIZ_Y_TAPS * N_PHASES]; + uint16_t UV_VCOEFS[N_VERT_UV_TAPS * N_PHASES]; + uint16_t RESERVEDF[0x100 / 2 - N_VERT_UV_TAPS * N_PHASES]; + uint16_t UV_HCOEFS[N_HORIZ_UV_TAPS * N_PHASES]; + uint16_t RESERVEDG[0x100 / 2 - N_HORIZ_UV_TAPS * N_PHASES]; +} OverlayBackBufferBlk; + +typedef struct { + uint8_t sign; + uint16_t mantissa; + uint8_t exponent; +} coeffRec, *coeffPtr; + + +} // namespace intel +} // nam + + +#endif diff --git a/merrifield/ips/common/OverlayPlaneBase.cpp b/merrifield/ips/common/OverlayPlaneBase.cpp new file mode 100644 index 0000000..5987b50 --- /dev/null +++ b/merrifield/ips/common/OverlayPlaneBase.cpp @@ -0,0 +1,1310 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <math.h> +#include <HwcTrace.h> +#include <Drm.h> +#include <Hwcomposer.h> +#include <PhysicalDevice.h> +#include <common/OverlayPlaneBase.h> +#include <common/TTMBufferMapper.h> +#include <common/GrallocSubBuffer.h> +#include <DisplayQuery.h> + + +// FIXME: remove it +#include <OMX_IVCommon.h> +#include <OMX_IntelVideoExt.h> + +namespace android { +namespace intel { + +OverlayPlaneBase::OverlayPlaneBase(int index, int disp) + : DisplayPlane(index, PLANE_OVERLAY, disp), + mTTMBuffers(), + mActiveTTMBuffers(), + mCurrent(0), + mWsbm(0), + mPipeConfig(0), + mBobDeinterlace(0), + mUseScaledBuffer(0) +{ + CTRACE(); + for (int i = 0; i < OVERLAY_BACK_BUFFER_COUNT; i++) { + mBackBuffer[i] = 0; + } +} + +OverlayPlaneBase::~OverlayPlaneBase() +{ + CTRACE(); +} + +bool OverlayPlaneBase::initialize(uint32_t bufferCount) +{ + Drm *drm = Hwcomposer::getInstance().getDrm(); + CTRACE(); + + // NOTE: use overlay's data buffer count for the overlay plane + if (bufferCount < OVERLAY_DATA_BUFFER_COUNT) { + ITRACE("override overlay buffer count from %d to %d", + bufferCount, OVERLAY_DATA_BUFFER_COUNT); + bufferCount = OVERLAY_DATA_BUFFER_COUNT; + } + if (!DisplayPlane::initialize(bufferCount)) { + DEINIT_AND_RETURN_FALSE("failed to initialize display plane"); + } + + mTTMBuffers.setCapacity(bufferCount); + mActiveTTMBuffers.setCapacity(MIN_DATA_BUFFER_COUNT); + + // init wsbm + mWsbm = new Wsbm(drm->getDrmFd()); + if (!mWsbm || !mWsbm->initialize()) { + DEINIT_AND_RETURN_FALSE("failed to create wsbm"); + } + + // create overlay back buffer + for (int i = 0; i < OVERLAY_BACK_BUFFER_COUNT; i++) { + mBackBuffer[i] = createBackBuffer(); + if (!mBackBuffer[i]) { + DEINIT_AND_RETURN_FALSE("failed to create overlay back buffer"); + } + // reset back buffer + resetBackBuffer(i); + } + + // disable overlay when created + flush(PLANE_DISABLE); + + return true; +} + +bool OverlayPlaneBase::isDisabled() +{ + RETURN_FALSE_IF_NOT_INIT(); + + struct drm_psb_register_rw_arg arg; + memset(&arg, 0, sizeof(struct drm_psb_register_rw_arg)); + + arg.get_plane_state_mask = 1; + arg.plane.type = DC_OVERLAY_PLANE; + arg.plane.index = mIndex; + // pass the pipe index to check its enabled status + // now we can pass the device id directly since + // their values are just equal + arg.plane.ctx = mDevice; // not used in kernel + + Drm *drm = Hwcomposer::getInstance().getDrm(); + bool ret = drm->writeReadIoctl(DRM_PSB_REGISTER_RW, &arg, sizeof(arg)); + if (ret == false) { + WTRACE("overlay plane query failed with error code %d", ret); + return false; + } + + DTRACE("overlay %d status %s on device %d, current device %d", + mIndex, arg.plane.ctx ? "DISABLED" : "ENABLED", mDevice, mDevice); + + return arg.plane.ctx == PSB_DC_PLANE_DISABLED; +} + +void OverlayPlaneBase::deinitialize() +{ + if (mTTMBuffers.size()) { + invalidateBufferCache(); + } + + if (mActiveTTMBuffers.size() > 0) { + invalidateActiveTTMBuffers(); + } + + // delete back buffer + for (int i = 0; i < OVERLAY_BACK_BUFFER_COUNT; i++) { + if (mBackBuffer[i]) { + deleteBackBuffer(i); + mBackBuffer[i] = NULL; + } + } + DEINIT_AND_DELETE_OBJ(mWsbm); + + DisplayPlane::deinitialize(); +} + +void OverlayPlaneBase::invalidateBufferCache() +{ + // clear plane buffer cache + DisplayPlane::invalidateBufferCache(); + invalidateTTMBuffers(); +} + +bool OverlayPlaneBase::assignToDevice(int disp) +{ + uint32_t pipeConfig = 0; + + RETURN_FALSE_IF_NOT_INIT(); + VTRACE("overlay %d assigned to disp %d", mIndex, disp); + + switch (disp) { + case IDisplayDevice::DEVICE_EXTERNAL: + pipeConfig = (0x2 << 6); + break; + case IDisplayDevice::DEVICE_PRIMARY: + default: + pipeConfig = 0; + break; + } + + // if pipe switching happened, then disable overlay first + if (mPipeConfig != pipeConfig) { + DTRACE("overlay %d switched from %d to %d", mIndex, mDevice, disp); + disable(); + } + + mPipeConfig = pipeConfig; + DisplayPlane::assignToDevice(disp); + + enable(); + + return true; +} + +void OverlayPlaneBase::setZOrderConfig(ZOrderConfig& zorderConfig, + void *nativeConfig) +{ + CTRACE(); + + // setup overlay z order + int ovaZOrder = -1; + int ovcZOrder = -1; + for (size_t i = 0; i < zorderConfig.size(); i++) { + DisplayPlane *plane = zorderConfig[i]->plane; + if (plane->getType() == DisplayPlane::PLANE_OVERLAY) { + if (plane->getIndex() == 0) { + ovaZOrder = i; + } else if (plane->getIndex() == 1) { + ovcZOrder = i; + } + } + } + + for (int i = 0; i < OVERLAY_BACK_BUFFER_COUNT; i++) { + OverlayBackBufferBlk *backBuffer = mBackBuffer[i]->buf; + if (!backBuffer) + return; + + // force overlay c above overlay a + if ((ovaZOrder >= 0) && (ovaZOrder < ovcZOrder)) { + backBuffer->OCONFIG |= (1 << 15); + } else { + backBuffer->OCONFIG &= ~(1 << 15); + } + } +} + +bool OverlayPlaneBase::reset() +{ + RETURN_FALSE_IF_NOT_INIT(); + + DisplayPlane::reset(); + + // invalidate active TTM buffers + if (mActiveTTMBuffers.size() > 0) { + invalidateActiveTTMBuffers(); + } + + // reset back buffers + for (int i = 0; i < OVERLAY_BACK_BUFFER_COUNT; i++) { + resetBackBuffer(i); + } + return true; +} + +bool OverlayPlaneBase::enable() +{ + RETURN_FALSE_IF_NOT_INIT(); + for (int i = 0; i < OVERLAY_BACK_BUFFER_COUNT; i++) { + OverlayBackBufferBlk *backBuffer = mBackBuffer[i]->buf; + if (!backBuffer) + return false; + + if (backBuffer->OCMD & 0x1) + return true; + + backBuffer->OCMD |= 0x1; + } + + // flush + flush(PLANE_ENABLE); + return true; +} + +bool OverlayPlaneBase::disable() +{ + RETURN_FALSE_IF_NOT_INIT(); + for (int i = 0; i < OVERLAY_BACK_BUFFER_COUNT; i++) { + OverlayBackBufferBlk *backBuffer = mBackBuffer[i]->buf; + if (!backBuffer) + return false; + + if (!(backBuffer->OCMD & 0x1)) + return true; + + backBuffer->OCMD &= ~0x1; + } + + // flush + flush(PLANE_DISABLE); + return true; +} + +OverlayBackBuffer* OverlayPlaneBase::createBackBuffer() +{ + CTRACE(); + + // create back buffer + OverlayBackBuffer *backBuffer = (OverlayBackBuffer *)malloc(sizeof(OverlayBackBuffer)); + if (!backBuffer) { + ETRACE("failed to allocate back buffer"); + return 0; + } + + + int size = sizeof(OverlayBackBufferBlk); + int alignment = 64 * 1024; + void *wsbmBufferObject = 0; + bool ret = mWsbm->allocateTTMBuffer(size, alignment, &wsbmBufferObject); + if (ret == false) { + ETRACE("failed to allocate TTM buffer"); + return 0; + } + + void *virtAddr = mWsbm->getCPUAddress(wsbmBufferObject); + uint32_t gttOffsetInPage = mWsbm->getGttOffset(wsbmBufferObject); + + backBuffer->buf = (OverlayBackBufferBlk *)virtAddr; + backBuffer->gttOffsetInPage = gttOffsetInPage; + backBuffer->bufObject = wsbmBufferObject; + + VTRACE("cpu %p, gtt %d", virtAddr, gttOffsetInPage); + + return backBuffer; +} + +void OverlayPlaneBase::deleteBackBuffer(int buf) +{ + if (!mBackBuffer[buf]) + return; + + void *wsbmBufferObject = mBackBuffer[buf]->bufObject; + bool ret = mWsbm->destroyTTMBuffer(wsbmBufferObject); + if (ret == false) { + WTRACE("failed to destroy TTM buffer"); + } + // free back buffer + free(mBackBuffer[buf]); + mBackBuffer[buf] = 0; +} + +void OverlayPlaneBase::resetBackBuffer(int buf) +{ + CTRACE(); + + if (!mBackBuffer[buf] || !mBackBuffer[buf]->buf) + return; + + OverlayBackBufferBlk *backBuffer = mBackBuffer[buf]->buf; + + memset(backBuffer, 0, sizeof(OverlayBackBufferBlk)); + + // reset overlay + backBuffer->OCLRC0 = (OVERLAY_INIT_CONTRAST << 18) | + (OVERLAY_INIT_BRIGHTNESS & 0xff); + backBuffer->OCLRC1 = OVERLAY_INIT_SATURATION; + backBuffer->DCLRKV = OVERLAY_INIT_COLORKEY; + backBuffer->DCLRKM = OVERLAY_INIT_COLORKEYMASK; + backBuffer->OCONFIG = 0; + backBuffer->OCONFIG |= (0x1 << 3); + backBuffer->OCONFIG |= (0x1 << 27); + backBuffer->SCHRKEN &= ~(0x7 << 24); + backBuffer->SCHRKEN |= 0xff; +} + +BufferMapper* OverlayPlaneBase::getTTMMapper(BufferMapper& grallocMapper, struct VideoPayloadBuffer *payload) +{ + buffer_handle_t khandle; + uint32_t w, h; + uint32_t yStride, uvStride; + stride_t stride; + int srcX, srcY, srcW, srcH; + int tmp; + + DataBuffer *buf; + ssize_t index; + TTMBufferMapper *mapper; + bool ret; + + if (!payload) { + ETRACE("invalid payload buffer"); + return 0; + } + + srcX = grallocMapper.getCrop().x; + srcY = grallocMapper.getCrop().y; + srcW = grallocMapper.getCrop().w; + srcH = grallocMapper.getCrop().h; + + // init ttm buffer + if (mUseScaledBuffer) { + khandle = payload->scaling_khandle; + } else { + khandle = payload->rotated_buffer_handle; + } + index = mTTMBuffers.indexOfKey(khandle); + if (index < 0) { + VTRACE("unmapped TTM buffer, will map it"); + + if (mUseScaledBuffer) { + w = payload->scaling_width; + h = payload->scaling_height; + } else { + w = payload->rotated_width; + h = payload->rotated_height; + + checkCrop(srcX, srcY, srcW, srcH, payload->coded_width, payload->coded_height); + } + + uint32_t format = grallocMapper.getFormat(); + // this is for sw decode with tiled buffer in landscape mode + if (payload->tiling) + format = OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar_Tiled; + + // calculate stride + switch (format) { + case HAL_PIXEL_FORMAT_YV12: + case HAL_PIXEL_FORMAT_I420: + uint32_t yStride_align; + yStride_align = DisplayQuery::getOverlayLumaStrideAlignment(grallocMapper.getFormat()); + if (yStride_align > 0) + { + yStride = align_to(align_to(w, 32), yStride_align); + } + else + { + yStride = align_to(align_to(w, 32), 64); + } + uvStride = align_to(yStride >> 1, 64); + stride.yuv.yStride = yStride; + stride.yuv.uvStride = uvStride; + break; + case HAL_PIXEL_FORMAT_NV12: + yStride = align_to(align_to(w, 32), 64); + uvStride = yStride; + stride.yuv.yStride = yStride; + stride.yuv.uvStride = uvStride; + break; + case OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar: + case OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar_Tiled: + if (mUseScaledBuffer) { + stride.yuv.yStride = payload->scaling_luma_stride; + stride.yuv.uvStride = payload->scaling_chroma_u_stride; + } else { + yStride = align_to(align_to(w, 32), 64); + uvStride = yStride; + stride.yuv.yStride = yStride; + stride.yuv.uvStride = uvStride; + } + break; + case HAL_PIXEL_FORMAT_YUY2: + case HAL_PIXEL_FORMAT_UYVY: + yStride = align_to((align_to(w, 32) << 1), 64); + uvStride = 0; + stride.yuv.yStride = yStride; + stride.yuv.uvStride = uvStride; + break; + } + + DataBuffer buf(khandle); + // update buffer + buf.setStride(stride); + buf.setWidth(w); + buf.setHeight(h); + buf.setCrop(srcX, srcY, srcW, srcH); + buf.setFormat(format); + + // create buffer mapper + bool res = false; + do { + mapper = new TTMBufferMapper(*mWsbm, buf); + if (!mapper) { + ETRACE("failed to allocate mapper"); + break; + } + // map ttm buffer + ret = mapper->map(); + if (!ret) { + ETRACE("failed to map"); + invalidateTTMBuffers(); + ret = mapper->map(); + if (!ret) { + ETRACE("failed to remap"); + break; + } + } + + if (mTTMBuffers.size() >= OVERLAY_DATA_BUFFER_COUNT) { + invalidateTTMBuffers(); + } + + // add mapper + index = mTTMBuffers.add(khandle, mapper); + if (index < 0) { + ETRACE("failed to add TTMMapper"); + break; + } + + // increase mapper refCount since it is added to mTTMBuffers + mapper->incRef(); + res = true; + } while (0); + + if (!res) { + // error handling + if (mapper) { + mapper->unmap(); + delete mapper; + mapper = NULL; + } + return 0; + } + } else { + VTRACE("got mapper in saved ttm buffers"); + mapper = reinterpret_cast<TTMBufferMapper *>(mTTMBuffers.valueAt(index)); + if (mapper->getCrop().x != srcX || mapper->getCrop().y != srcY || + mapper->getCrop().w != srcW || mapper->getCrop().h != srcH) { + if(!mUseScaledBuffer) + checkCrop(srcX, srcY, srcW, srcH, payload->coded_width, payload->coded_height); + mapper->setCrop(srcX, srcY, srcW, srcH); + } + } + + XTRACE(); + return mapper; +} + +void OverlayPlaneBase::putTTMMapper(BufferMapper* mapper) +{ + if (!mapper) + return; + + if (!mapper->decRef()) { + // unmap it + mapper->unmap(); + + // destroy this mapper + delete mapper; + } +} + +bool OverlayPlaneBase::isActiveTTMBuffer(BufferMapper *mapper) +{ + for (size_t i = 0; i < mActiveTTMBuffers.size(); i++) { + BufferMapper *activeMapper = mActiveTTMBuffers.itemAt(i); + if (!activeMapper) + continue; + if (activeMapper->getKey() == mapper->getKey()) + return true; + } + + return false; +} + +void OverlayPlaneBase::updateActiveTTMBuffers(BufferMapper *mapper) +{ + // unmap the first entry (oldest buffer) + if (mActiveTTMBuffers.size() >= MAX_ACTIVE_TTM_BUFFERS) { + BufferMapper *oldest = mActiveTTMBuffers.itemAt(0); + putTTMMapper(oldest); + mActiveTTMBuffers.removeAt(0); + } + + // queue it to cached buffers + if (!isActiveTTMBuffer(mapper)) { + mapper->incRef(); + mActiveTTMBuffers.push_back(mapper); + } +} + +void OverlayPlaneBase::invalidateActiveTTMBuffers() +{ + BufferMapper* mapper; + + RETURN_VOID_IF_NOT_INIT(); + + for (size_t i = 0; i < mActiveTTMBuffers.size(); i++) { + mapper = mActiveTTMBuffers.itemAt(i); + // unmap it + putTTMMapper(mapper); + } + + // clear recorded data buffers + mActiveTTMBuffers.clear(); +} + +void OverlayPlaneBase::invalidateTTMBuffers() +{ + BufferMapper* mapper; + for (size_t i = 0; i < mTTMBuffers.size(); i++) { + mapper = mTTMBuffers.valueAt(i); + // putTTMMapper removes mapper from cache + putTTMMapper(mapper); + } + mTTMBuffers.clear(); +} + + +bool OverlayPlaneBase::rotatedBufferReady(BufferMapper& mapper, BufferMapper* &rotatedMapper) +{ + struct VideoPayloadBuffer *payload; + uint32_t format; + + // only NV12_VED has rotated buffer + format = mapper.getFormat(); + if (format != OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar && + format != OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar_Tiled) + return false; + + payload = (struct VideoPayloadBuffer *)mapper.getCpuAddress(SUB_BUFFER1); + // check payload + if (!payload) { + ETRACE("no payload found"); + return false; + } + + if (payload->force_output_method == FORCE_OUTPUT_GPU) + return false; + + if (payload->client_transform != mTransform) { + if (payload->surface_protected) { + payload->hwc_timestamp = systemTime(); + payload->layer_transform = mTransform; + } + WTRACE("client is not ready"); + return false; + } + + rotatedMapper = getTTMMapper(mapper, payload); + return true; +} + + +bool OverlayPlaneBase::useOverlayRotation(BufferMapper& mapper) +{ + // by default overlay plane does not support rotation. + return false; +} + +bool OverlayPlaneBase::scaledBufferReady(BufferMapper& mapper, BufferMapper* &scaledMapper, VideoPayloadBuffer *payload) +{ + return false; +} + +void OverlayPlaneBase::checkPosition(int& x, int& y, int& w, int& h) +{ + drmModeModeInfoPtr mode = &mModeInfo; + + if (mode->hdisplay == 0 || mode->vdisplay == 0) + return; + + if (x < 0) + x = 0; + if (y < 0) + y = 0; + if ((x + w) > mode->hdisplay) + w = mode->hdisplay - x; + if ((y + h) > mode->vdisplay) + h = mode->vdisplay - y; +} + +void OverlayPlaneBase::checkCrop(int& srcX, int& srcY, int& srcW, int& srcH, + int coded_width, int coded_height) +{ + int tmp; + + if (mTransform) + srcH >>= mBobDeinterlace; + + if (mTransform == HWC_TRANSFORM_ROT_90 || mTransform == HWC_TRANSFORM_ROT_270) { + tmp = srcH; + srcH = srcW; + srcW = tmp; + + tmp = srcX; + srcX = srcY; + srcY = tmp; + + tmp = coded_width; + coded_width = coded_height; + coded_height = tmp; + } + + // skip pading bytes in rotate buffer + switch(mTransform) { + case HWC_TRANSFORM_ROT_90: + srcX = (coded_width >> mBobDeinterlace) - srcW - srcX; + break; + case HWC_TRANSFORM_ROT_180: + srcX = coded_width - srcW - srcX; + srcY = (coded_height >> mBobDeinterlace) - srcH - srcY; + break; + case HWC_TRANSFORM_ROT_270: + srcY = coded_height - srcH - srcY; + break; + default: + break; + } +} + + +bool OverlayPlaneBase::bufferOffsetSetup(BufferMapper& mapper) +{ + CTRACE(); + + OverlayBackBufferBlk *backBuffer = mBackBuffer[mCurrent]->buf; + if (!backBuffer) { + ETRACE("invalid back buffer"); + return false; + } + + uint32_t format = mapper.getFormat(); + uint32_t gttOffsetInBytes = (mapper.getGttOffsetInPage(0) << 12); + uint32_t yStride = mapper.getStride().yuv.yStride; + uint32_t uvStride = mapper.getStride().yuv.uvStride; + uint32_t w = mapper.getWidth(); + uint32_t h = mapper.getHeight(); + uint32_t srcX= mapper.getCrop().x; + uint32_t srcY= mapper.getCrop().y; + + // clear original format setting + backBuffer->OCMD &= ~(0xf << 10); + backBuffer->OCMD &= ~OVERLAY_MEMORY_LAYOUT_TILED; + + // Y/U/V plane must be 4k bytes aligned. + backBuffer->OSTART_0Y = gttOffsetInBytes; + if (mIsProtectedBuffer) { + // temporary workaround until vsync event logic is corrected. + // it seems that overlay buffer update and renderring can be overlapped, + // as such encryption bit may be cleared during HW rendering + backBuffer->OSTART_0Y |= 0x01; + } + + backBuffer->OSTART_0U = gttOffsetInBytes; + backBuffer->OSTART_0V = gttOffsetInBytes; + + backBuffer->OSTART_1Y = backBuffer->OSTART_0Y; + backBuffer->OSTART_1U = backBuffer->OSTART_0U; + backBuffer->OSTART_1V = backBuffer->OSTART_0V; + + switch(format) { + case HAL_PIXEL_FORMAT_YV12: // YV12 + backBuffer->OBUF_0Y = 0; + backBuffer->OBUF_0V = yStride * h; + backBuffer->OBUF_0U = backBuffer->OBUF_0V + (uvStride * (h / 2)); + backBuffer->OCMD |= OVERLAY_FORMAT_PLANAR_YUV420; + break; + case HAL_PIXEL_FORMAT_I420: // I420 + backBuffer->OBUF_0Y = 0; + backBuffer->OBUF_0U = yStride * h; + backBuffer->OBUF_0V = backBuffer->OBUF_0U + (uvStride * (h / 2)); + backBuffer->OCMD |= OVERLAY_FORMAT_PLANAR_YUV420; + break; + case HAL_PIXEL_FORMAT_NV12: // NV12 + backBuffer->OBUF_0Y = 0; + backBuffer->OBUF_0U = yStride * h; + backBuffer->OBUF_0V = 0; + backBuffer->OCMD |= OVERLAY_FORMAT_PLANAR_NV12_2; + break; + // NOTE: this is the decoded video format, align the height to 32B + //as it's defined by video driver + case OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar: // Intel codec NV12 + backBuffer->OBUF_0Y = 0; + backBuffer->OBUF_0U = yStride * align_to(h, 32); + backBuffer->OBUF_0V = 0; + backBuffer->OCMD |= OVERLAY_FORMAT_PLANAR_NV12_2; + break; + case OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar_Tiled: //NV12_tiled + backBuffer->OBUF_0Y = 0; + backBuffer->OBUF_0U = yStride * align_to(h, 32); + backBuffer->OBUF_0V = 0; + backBuffer->OSTART_0U += yStride * align_to(h, 32); + backBuffer->OSTART_0V += yStride * align_to(h, 32); + backBuffer->OSTART_1U = backBuffer->OSTART_0U; + backBuffer->OSTART_1V = backBuffer->OSTART_0V; + backBuffer->OTILEOFF_0Y = srcX + (srcY << 16); + backBuffer->OTILEOFF_1Y = backBuffer->OTILEOFF_0Y; + backBuffer->OTILEOFF_0U = srcX + ((srcY / 2) << 16); + backBuffer->OTILEOFF_1U = backBuffer->OTILEOFF_0U; + backBuffer->OTILEOFF_0V = backBuffer->OTILEOFF_0U; + backBuffer->OTILEOFF_1V = backBuffer->OTILEOFF_0U; + backBuffer->OCMD |= OVERLAY_FORMAT_PLANAR_NV12_2; + backBuffer->OCMD |= OVERLAY_MEMORY_LAYOUT_TILED; + break; + case HAL_PIXEL_FORMAT_YUY2: // YUY2 + backBuffer->OBUF_0Y = 0; + backBuffer->OBUF_0U = 0; + backBuffer->OBUF_0V = 0; + backBuffer->OCMD |= OVERLAY_FORMAT_PACKED_YUV422; + backBuffer->OCMD |= OVERLAY_PACKED_ORDER_YUY2; + break; + case HAL_PIXEL_FORMAT_UYVY: // UYVY + backBuffer->OBUF_0Y = 0; + backBuffer->OBUF_0U = 0; + backBuffer->OBUF_0V = 0; + backBuffer->OCMD |= OVERLAY_FORMAT_PACKED_YUV422; + backBuffer->OCMD |= OVERLAY_PACKED_ORDER_UYVY; + break; + default: + ETRACE("unsupported format %d", format); + return false; + } + + backBuffer->OBUF_0Y += srcY * yStride + srcX; + backBuffer->OBUF_0V += (srcY / 2) * uvStride + srcX; + backBuffer->OBUF_0U += (srcY / 2) * uvStride + srcX; + backBuffer->OBUF_1Y = backBuffer->OBUF_0Y; + backBuffer->OBUF_1U = backBuffer->OBUF_0U; + backBuffer->OBUF_1V = backBuffer->OBUF_0V; + + VTRACE("done. offset (%d, %d, %d)", + backBuffer->OBUF_0Y, + backBuffer->OBUF_0U, + backBuffer->OBUF_0V); + return true; +} + +uint32_t OverlayPlaneBase::calculateSWidthSW(uint32_t offset, uint32_t width) +{ + ATRACE("offset = %d, width = %d", offset, width); + + uint32_t swidth = ((offset + width + 0x3F) >> 6) - (offset >> 6); + + swidth <<= 1; + swidth -= 1; + + return swidth; +} + +bool OverlayPlaneBase::coordinateSetup(BufferMapper& mapper) +{ + CTRACE(); + + OverlayBackBufferBlk *backBuffer = mBackBuffer[mCurrent]->buf; + if (!backBuffer) { + ETRACE("invalid back buffer"); + return false; + } + + uint32_t swidthy = 0; + uint32_t swidthuv = 0; + uint32_t format = mapper.getFormat(); + uint32_t width = mapper.getCrop().w; + uint32_t height = mapper.getCrop().h; + uint32_t yStride = mapper.getStride().yuv.yStride; + uint32_t uvStride = mapper.getStride().yuv.uvStride; + uint32_t offsety = backBuffer->OBUF_0Y; + uint32_t offsetu = backBuffer->OBUF_0U; + + switch (format) { + case HAL_PIXEL_FORMAT_YV12: // YV12 + case HAL_PIXEL_FORMAT_I420: // I420 + case HAL_PIXEL_FORMAT_NV12: // NV12 + case OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar: // NV12 + case OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar_Tiled: // NV12_tiled + break; + case HAL_PIXEL_FORMAT_YUY2: // YUY2 + case HAL_PIXEL_FORMAT_UYVY: // UYVY + width <<= 1; + break; + default: + ETRACE("unsupported format %d", format); + return false; + } + + if (width <= 0 || height <= 0) { + ETRACE("invalid src dim"); + return false; + } + + if (yStride <=0 && uvStride <= 0) { + ETRACE("invalid source stride"); + return false; + } + + backBuffer->SWIDTH = width | ((width / 2) << 16); + swidthy = calculateSWidthSW(offsety, width); + swidthuv = calculateSWidthSW(offsetu, width / 2); + backBuffer->SWIDTHSW = (swidthy << 2) | (swidthuv << 18); + backBuffer->SHEIGHT = height | ((height / 2) << 16); + backBuffer->OSTRIDE = (yStride & (~0x3f)) | ((uvStride & (~0x3f)) << 16); + + XTRACE(); + + return true; +} + +bool OverlayPlaneBase::setCoeffRegs(double *coeff, int mantSize, + coeffPtr pCoeff, int pos) +{ + int maxVal, icoeff, res; + int sign; + double c; + + sign = 0; + maxVal = 1 << mantSize; + c = *coeff; + if (c < 0.0) { + sign = 1; + c = -c; + } + + res = 12 - mantSize; + if ((icoeff = (int)(c * 4 * maxVal + 0.5)) < maxVal) { + pCoeff[pos].exponent = 3; + pCoeff[pos].mantissa = icoeff << res; + *coeff = (double)icoeff / (double)(4 * maxVal); + } else if ((icoeff = (int)(c * 2 * maxVal + 0.5)) < maxVal) { + pCoeff[pos].exponent = 2; + pCoeff[pos].mantissa = icoeff << res; + *coeff = (double)icoeff / (double)(2 * maxVal); + } else if ((icoeff = (int)(c * maxVal + 0.5)) < maxVal) { + pCoeff[pos].exponent = 1; + pCoeff[pos].mantissa = icoeff << res; + *coeff = (double)icoeff / (double)(maxVal); + } else if ((icoeff = (int)(c * maxVal * 0.5 + 0.5)) < maxVal) { + pCoeff[pos].exponent = 0; + pCoeff[pos].mantissa = icoeff << res; + *coeff = (double)icoeff / (double)(maxVal / 2); + } else { + // Coeff out of range + return false; + } + + pCoeff[pos].sign = sign; + if (sign) + *coeff = -(*coeff); + return true; +} + +void OverlayPlaneBase::updateCoeff(int taps, double fCutoff, + bool isHoriz, bool isY, + coeffPtr pCoeff) +{ + int i, j, j1, num, pos, mantSize; + double pi = 3.1415926535, val, sinc, window, sum; + double rawCoeff[MAX_TAPS * 32], coeffs[N_PHASES][MAX_TAPS]; + double diff; + int tapAdjust[MAX_TAPS], tap2Fix; + bool isVertAndUV; + + if (isHoriz) + mantSize = 7; + else + mantSize = 6; + + isVertAndUV = !isHoriz && !isY; + num = taps * 16; + for (i = 0; i < num * 2; i++) { + val = (1.0 / fCutoff) * taps * pi * (i - num) / (2 * num); + if (val == 0.0) + sinc = 1.0; + else + sinc = sin(val) / val; + + // Hamming window + window = (0.54 - 0.46 * cos(2 * i * pi / (2 * num - 1))); + rawCoeff[i] = sinc * window; + } + + for (i = 0; i < N_PHASES; i++) { + // Normalise the coefficients + sum = 0.0; + for (j = 0; j < taps; j++) { + pos = i + j * 32; + sum += rawCoeff[pos]; + } + for (j = 0; j < taps; j++) { + pos = i + j * 32; + coeffs[i][j] = rawCoeff[pos] / sum; + } + + // Set the register values + for (j = 0; j < taps; j++) { + pos = j + i * taps; + if ((j == (taps - 1) / 2) && !isVertAndUV) + setCoeffRegs(&coeffs[i][j], mantSize + 2, pCoeff, pos); + else + setCoeffRegs(&coeffs[i][j], mantSize, pCoeff, pos); + } + + tapAdjust[0] = (taps - 1) / 2; + for (j = 1, j1 = 1; j <= tapAdjust[0]; j++, j1++) { + tapAdjust[j1] = tapAdjust[0] - j; + tapAdjust[++j1] = tapAdjust[0] + j; + } + + // Adjust the coefficients + sum = 0.0; + for (j = 0; j < taps; j++) + sum += coeffs[i][j]; + if (sum != 1.0) { + for (j1 = 0; j1 < taps; j1++) { + tap2Fix = tapAdjust[j1]; + diff = 1.0 - sum; + coeffs[i][tap2Fix] += diff; + pos = tap2Fix + i * taps; + if ((tap2Fix == (taps - 1) / 2) && !isVertAndUV) + setCoeffRegs(&coeffs[i][tap2Fix], mantSize + 2, pCoeff, pos); + else + setCoeffRegs(&coeffs[i][tap2Fix], mantSize, pCoeff, pos); + + sum = 0.0; + for (j = 0; j < taps; j++) + sum += coeffs[i][j]; + if (sum == 1.0) + break; + } + } + } +} + +bool OverlayPlaneBase::scalingSetup(BufferMapper& mapper) +{ + int xscaleInt, xscaleFract, yscaleInt, yscaleFract; + int xscaleIntUV, xscaleFractUV; + int yscaleIntUV, yscaleFractUV; + int deinterlace_factor = 1; + // UV is half the size of Y -- YUV420 + int uvratio = 2; + uint32_t newval; + coeffRec xcoeffY[N_HORIZ_Y_TAPS * N_PHASES]; + coeffRec xcoeffUV[N_HORIZ_UV_TAPS * N_PHASES]; + int i, j, pos; + bool scaleChanged = false; + int x, y, w, h; + + OverlayBackBufferBlk *backBuffer = mBackBuffer[mCurrent]->buf; + if (!backBuffer) { + ETRACE("invalid back buffer"); + return false; + } + + x = mPosition.x; + y = mPosition.y; + w = mPosition.w; + h = mPosition.h; + + // check position + checkPosition(x, y, w, h); + VTRACE("final position (%d, %d, %d, %d)", x, y, w, h); + + if ((w <= 0) || (h <= 0)) { + ETRACE("invalid dst width/height"); + return false; + } + + // setup dst position + backBuffer->DWINPOS = (y << 16) | x; + backBuffer->DWINSZ = (h << 16) | w; + + uint32_t srcWidth = mapper.getCrop().w; + uint32_t srcHeight = mapper.getCrop().h; + uint32_t dstWidth = w; + uint32_t dstHeight = h; + + if (mBobDeinterlace && !mTransform) + deinterlace_factor = 2; + + VTRACE("src (%dx%d), dst (%dx%d)", + srcWidth, srcHeight, + dstWidth, dstHeight); + + // Y down-scale factor as a multiple of 4096 + if (srcWidth == dstWidth && srcHeight == dstHeight) { + xscaleFract = (1 << 12); + yscaleFract = (1 << 12)/deinterlace_factor; + } else { + xscaleFract = ((srcWidth - 1) << 12) / dstWidth; + yscaleFract = ((srcHeight - 1) << 12) / (dstHeight * deinterlace_factor); + } + + // Calculate the UV scaling factor + xscaleFractUV = xscaleFract / uvratio; + yscaleFractUV = yscaleFract / uvratio; + + // To keep the relative Y and UV ratios exact, round the Y scales + // to a multiple of the Y/UV ratio. + xscaleFract = xscaleFractUV * uvratio; + yscaleFract = yscaleFractUV * uvratio; + + // Integer (un-multiplied) values + xscaleInt = xscaleFract >> 12; + yscaleInt = yscaleFract >> 12; + + xscaleIntUV = xscaleFractUV >> 12; + yscaleIntUV = yscaleFractUV >> 12; + + // Check scaling ratio + if (xscaleInt > INTEL_OVERLAY_MAX_SCALING_RATIO) { + ETRACE("xscaleInt > %d", INTEL_OVERLAY_MAX_SCALING_RATIO); + return false; + } + + // shouldn't get here + if (xscaleIntUV > INTEL_OVERLAY_MAX_SCALING_RATIO) { + ETRACE("xscaleIntUV > %d", INTEL_OVERLAY_MAX_SCALING_RATIO); + return false; + } + + newval = (xscaleInt << 15) | + ((xscaleFract & 0xFFF) << 3) | ((yscaleFract & 0xFFF) << 20); + if (newval != backBuffer->YRGBSCALE) { + scaleChanged = true; + backBuffer->YRGBSCALE = newval; + } + + newval = (xscaleIntUV << 15) | ((xscaleFractUV & 0xFFF) << 3) | + ((yscaleFractUV & 0xFFF) << 20); + if (newval != backBuffer->UVSCALE) { + scaleChanged = true; + backBuffer->UVSCALE = newval; + } + + newval = yscaleInt << 16 | yscaleIntUV; + if (newval != backBuffer->UVSCALEV) { + scaleChanged = true; + backBuffer->UVSCALEV = newval; + } + + // Recalculate coefficients if the scaling changed + // Only Horizontal coefficients so far. + if (scaleChanged) { + double fCutoffY; + double fCutoffUV; + + fCutoffY = xscaleFract / 4096.0; + fCutoffUV = xscaleFractUV / 4096.0; + + // Limit to between 1.0 and 3.0 + if (fCutoffY < MIN_CUTOFF_FREQ) + fCutoffY = MIN_CUTOFF_FREQ; + if (fCutoffY > MAX_CUTOFF_FREQ) + fCutoffY = MAX_CUTOFF_FREQ; + if (fCutoffUV < MIN_CUTOFF_FREQ) + fCutoffUV = MIN_CUTOFF_FREQ; + if (fCutoffUV > MAX_CUTOFF_FREQ) + fCutoffUV = MAX_CUTOFF_FREQ; + + updateCoeff(N_HORIZ_Y_TAPS, fCutoffY, true, true, xcoeffY); + updateCoeff(N_HORIZ_UV_TAPS, fCutoffUV, true, false, xcoeffUV); + + for (i = 0; i < N_PHASES; i++) { + for (j = 0; j < N_HORIZ_Y_TAPS; j++) { + pos = i * N_HORIZ_Y_TAPS + j; + backBuffer->Y_HCOEFS[pos] = + (xcoeffY[pos].sign << 15 | + xcoeffY[pos].exponent << 12 | + xcoeffY[pos].mantissa); + } + } + for (i = 0; i < N_PHASES; i++) { + for (j = 0; j < N_HORIZ_UV_TAPS; j++) { + pos = i * N_HORIZ_UV_TAPS + j; + backBuffer->UV_HCOEFS[pos] = + (xcoeffUV[pos].sign << 15 | + xcoeffUV[pos].exponent << 12 | + xcoeffUV[pos].mantissa); + } + } + } + + XTRACE(); + return true; +} + +bool OverlayPlaneBase::colorSetup(BufferMapper& mapper) +{ + CTRACE(); + + OverlayBackBufferBlk *backBuffer = mBackBuffer[mCurrent]->buf; + if (!backBuffer) { + ETRACE("invalid back buffer"); + return false; + } + + uint32_t format = mapper.getFormat(); + if (format != OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar && + format != OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar_Tiled) { + + VTRACE("Not video layer, use default color setting"); + backBuffer->OCLRC0 = (OVERLAY_INIT_CONTRAST << 18) | + (OVERLAY_INIT_BRIGHTNESS & 0xff); + backBuffer->OCLRC1 = OVERLAY_INIT_SATURATION; + backBuffer->OCONFIG &= ~(1 << 5); + + return true; + } + + struct VideoPayloadBuffer *payload; + payload = (struct VideoPayloadBuffer *)mapper.getCpuAddress(SUB_BUFFER1); + // check payload + if (!payload) { + ETRACE("no payload found"); + return false; + } + + // BT.601 or BT.709 + backBuffer->OCONFIG &= ~(1 << 5); + backBuffer->OCONFIG |= ((payload->csc_mode & 1) << 5); + + // no level expansion for video on HDMI + if (payload->video_range || mPipeConfig == (0x2 << 6)) { + // full range, no need to do level expansion + backBuffer->OCLRC0 = 0x1000000; + backBuffer->OCLRC1 = 0x80; + } else { + // level expansion for limited range + backBuffer->OCLRC0 = (OVERLAY_INIT_CONTRAST << 18) | + (OVERLAY_INIT_BRIGHTNESS & 0xff); + backBuffer->OCLRC1 = OVERLAY_INIT_SATURATION; + } + + return true; +} + +bool OverlayPlaneBase::setDataBuffer(BufferMapper& grallocMapper) +{ + BufferMapper *mapper; + BufferMapper *videoBufferMapper = 0; + bool ret; + uint32_t format; + + RETURN_FALSE_IF_NOT_INIT(); + + // get gralloc mapper + mapper = &grallocMapper; + format = grallocMapper.getFormat(); + if (format == OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar || + format == OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar_Tiled) { + struct VideoPayloadBuffer *payload; + payload = (struct VideoPayloadBuffer *)grallocMapper.getCpuAddress(SUB_BUFFER1); + if (!payload) { + ETRACE("invalid payload buffer"); + return 0; + } + + mBobDeinterlace = payload->bob_deinterlace; + + int srcW, srcH; + srcW = grallocMapper.getCrop().w - grallocMapper.getCrop().x; + srcH = grallocMapper.getCrop().h - grallocMapper.getCrop().y; + if ((srcW > INTEL_OVERLAY_MAX_WIDTH - 1) || (srcH > INTEL_OVERLAY_MAX_HEIGHT - 1)) { + if (mTransform) { + int x, y, w, h; + x = mSrcCrop.x; + y = mSrcCrop.y; + w = mSrcCrop.w; + h = mSrcCrop.h; + setSourceCrop(0, 0, payload->scaling_width, payload->scaling_height); + if (!useOverlayRotation(grallocMapper)) { + DTRACE("The scaled buffer will hit overlay rotation limitation, fall back to GLES"); + setSourceCrop(x, y, w, h); + return false; + } + } + + if (!scaledBufferReady(grallocMapper, videoBufferMapper, payload)) { + DTRACE("scaled buffer is not ready, fall back to GLES"); + return false; + } else { + videoBufferMapper->setFormat(OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar); + mapper = videoBufferMapper; + } + } + } + + if (!mUseScaledBuffer && mTransform && !useOverlayRotation(grallocMapper)) { + if (!rotatedBufferReady(grallocMapper, videoBufferMapper)) { + DTRACE("rotated buffer is not ready"); + return false; + } + + if (!videoBufferMapper) { + ETRACE("failed to get rotated buffer"); + return false; + } + mapper = videoBufferMapper; + } + + OverlayBackBufferBlk *backBuffer = mBackBuffer[mCurrent]->buf; + if (!backBuffer) { + ETRACE("invalid back buffer"); + return false; + } + + ret = bufferOffsetSetup(*mapper); + if (ret == false) { + ETRACE("failed to set up buffer offsets"); + return false; + } + + ret = coordinateSetup(*mapper); + if (ret == false) { + ETRACE("failed to set up overlay coordinates"); + return false; + } + + ret = scalingSetup(*mapper); + if (ret == false) { + ETRACE("failed to set up scaling parameters"); + return false; + } + + backBuffer->OCMD |= 0x1; + + ret = colorSetup(grallocMapper); + if (ret == false) { + ETRACE("failed to set up color parameters"); + return false; + } + if (mBobDeinterlace && !mTransform) { + backBuffer->OCMD |= BUF_TYPE_FIELD; + backBuffer->OCMD &= ~FIELD_SELECT; + backBuffer->OCMD |= FIELD0; + backBuffer->OCMD &= ~(BUFFER_SELECT); + backBuffer->OCMD |= BUFFER0; + } + + // add to active ttm buffers if it's a rotated buffer + if (videoBufferMapper) { + updateActiveTTMBuffers(mapper); + } + + mUseScaledBuffer = 0; + return true; +} + +} // namespace intel +} // namespace android + diff --git a/merrifield/ips/common/OverlayPlaneBase.h b/merrifield/ips/common/OverlayPlaneBase.h new file mode 100644 index 0000000..fc76a61 --- /dev/null +++ b/merrifield/ips/common/OverlayPlaneBase.h @@ -0,0 +1,129 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef OVERLAY_PLANE_BASE_H +#define OVERLAY_PLANE_BASE_H + +#include <utils/KeyedVector.h> +#include <hal_public.h> +#include <DisplayPlane.h> +#include <BufferMapper.h> +#include <common/Wsbm.h> +#include <common/OverlayHardware.h> +#include <common/VideoPayloadBuffer.h> + +namespace android { +namespace intel { + +typedef struct { + OverlayBackBufferBlk *buf; + uint32_t gttOffsetInPage; + void* bufObject; +} OverlayBackBuffer; + +class OverlayPlaneBase : public DisplayPlane { +public: + OverlayPlaneBase(int index, int disp); + virtual ~OverlayPlaneBase(); + + virtual void invalidateBufferCache(); + + virtual bool assignToDevice(int disp); + + virtual void setZOrderConfig(ZOrderConfig& config, void *nativeConfig); + + // plane operations + virtual bool flip(void *ctx) = 0; + virtual bool reset(); + virtual bool enable(); + virtual bool disable(); + virtual bool isDisabled(); + + virtual void* getContext() const = 0; + virtual bool initialize(uint32_t bufferCount); + virtual void deinitialize(); + +protected: + // generic overlay register flush + virtual bool flush(uint32_t flags) = 0; + virtual bool setDataBuffer(BufferMapper& mapper); + virtual bool bufferOffsetSetup(BufferMapper& mapper); + virtual uint32_t calculateSWidthSW(uint32_t offset, uint32_t width); + virtual bool coordinateSetup(BufferMapper& mapper); + virtual bool setCoeffRegs(double *coeff, int mantSize, + coeffPtr pCoeff, int pos); + virtual void updateCoeff(int taps, double fCutoff, + bool isHoriz, bool isY, + coeffPtr pCoeff); + virtual bool scalingSetup(BufferMapper& mapper); + virtual bool colorSetup(BufferMapper& mapper); + virtual void checkPosition(int& x, int& y, int& w, int& h); + virtual void checkCrop(int& x, int& y, int& w, int& h, int coded_width, int coded_height); + + +protected: + // back buffer operations + virtual OverlayBackBuffer* createBackBuffer(); + virtual void deleteBackBuffer(int buf); + virtual void resetBackBuffer(int buf); + + virtual BufferMapper* getTTMMapper(BufferMapper& grallocMapper, struct VideoPayloadBuffer *payload); + virtual void putTTMMapper(BufferMapper* mapper); + virtual bool rotatedBufferReady(BufferMapper& mapper, BufferMapper* &rotatedMapper); + virtual bool useOverlayRotation(BufferMapper& mapper); + virtual bool scaledBufferReady(BufferMapper& mapper, BufferMapper* &scaledMapper, VideoPayloadBuffer *payload); + +private: + inline bool isActiveTTMBuffer(BufferMapper *mapper); + void updateActiveTTMBuffers(BufferMapper *mapper); + void invalidateActiveTTMBuffers(); + void invalidateTTMBuffers(); + +protected: + // flush flags + enum { + PLANE_ENABLE = 0x00000001UL, + PLANE_DISABLE = 0x00000002UL, + UPDATE_COEF = 0x00000004UL, + }; + + enum { + OVERLAY_BACK_BUFFER_COUNT = 3, + MAX_ACTIVE_TTM_BUFFERS = 3, + OVERLAY_DATA_BUFFER_COUNT = 20, + }; + + // TTM data buffers + KeyedVector<buffer_handle_t, BufferMapper*> mTTMBuffers; + // latest TTM buffers + Vector<BufferMapper*> mActiveTTMBuffers; + + // overlay back buffer + OverlayBackBuffer *mBackBuffer[OVERLAY_BACK_BUFFER_COUNT]; + int mCurrent; + // wsbm + Wsbm *mWsbm; + // pipe config + uint32_t mPipeConfig; + + int mBobDeinterlace; + int mUseScaledBuffer; +}; + +} // namespace intel +} // namespace android + +#endif /* OVERLAY_PLANE_BASE_H */ + diff --git a/merrifield/ips/common/PixelFormat.cpp b/merrifield/ips/common/PixelFormat.cpp new file mode 100644 index 0000000..758788c --- /dev/null +++ b/merrifield/ips/common/PixelFormat.cpp @@ -0,0 +1,55 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <hal_public.h> +#include <HwcTrace.h> +#include <common/PixelFormat.h> + +namespace android { +namespace intel { + +bool PixelFormat::convertFormat(uint32_t grallocFormat, uint32_t& spriteFormat, int& bpp) +{ + switch (grallocFormat) { + case HAL_PIXEL_FORMAT_RGBA_8888: + spriteFormat = PLANE_PIXEL_FORMAT_RGBA8888; + bpp = 4; + break; + case HAL_PIXEL_FORMAT_RGBX_8888: + spriteFormat = PLANE_PIXEL_FORMAT_RGBX8888; + bpp = 4; + break; + case HAL_PIXEL_FORMAT_BGRX_8888: + spriteFormat = PLANE_PIXEL_FORMAT_BGRX8888; + bpp = 4; + break; + case HAL_PIXEL_FORMAT_BGRA_8888: + spriteFormat = PLANE_PIXEL_FORMAT_BGRA8888; + bpp = 4; + break; + case HAL_PIXEL_FORMAT_RGB_565: + spriteFormat = PLANE_PIXEL_FORMAT_BGRX565; + bpp = 2; + break; + default: + return false; + } + + return true; +} + + +} // namespace intel +} // namespace android diff --git a/merrifield/ips/common/PixelFormat.h b/merrifield/ips/common/PixelFormat.h new file mode 100644 index 0000000..cea69e0 --- /dev/null +++ b/merrifield/ips/common/PixelFormat.h @@ -0,0 +1,41 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef PIXEL_FORMAT_H +#define PIXEL_FORMAT_H + +namespace android { +namespace intel { + +class PixelFormat +{ +public: + enum { + PLANE_PIXEL_FORMAT_BGRX565 = 0x14000000UL, + PLANE_PIXEL_FORMAT_BGRX8888 = 0x18000000UL, + PLANE_PIXEL_FORMAT_BGRA8888 = 0x1c000000UL, + PLANE_PIXEL_FORMAT_RGBX8888 = 0x38000000UL, + PLANE_PIXEL_FORMAT_RGBA8888 = 0x3c000000UL, + }; + + // convert gralloc color format to IP specific sprite pixel format. + // See DSPACNTR (Display A Primary Sprite Control Register for more information) + static bool convertFormat(uint32_t grallocFormat, uint32_t& spriteFormat, int& bpp); +}; + +} // namespace intel +} // namespace android + +#endif /*PIXEL_FORMAT_H*/ diff --git a/merrifield/ips/common/PlaneCapabilities.cpp b/merrifield/ips/common/PlaneCapabilities.cpp new file mode 100644 index 0000000..973c535 --- /dev/null +++ b/merrifield/ips/common/PlaneCapabilities.cpp @@ -0,0 +1,234 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <HwcTrace.h> +#include <DisplayPlane.h> +#include <hal_public.h> +#include <OMX_IVCommon.h> +#include <OMX_IntelVideoExt.h> +#include <PlaneCapabilities.h> +#include "OverlayHardware.h" +#include <HwcLayer.h> + +#define SPRITE_PLANE_MAX_STRIDE_TILED 16384 +//FIXME: need confirmation about this stride +#define SPRITE_PLANE_MAX_STRIDE_LINEAR 8192 + +#define OVERLAY_PLANE_MAX_STRIDE_PACKED 4096 +#define OVERLAY_PLANE_MAX_STRIDE_LINEAR 8192 + +namespace android { +namespace intel { + +bool PlaneCapabilities::isFormatSupported(int planeType, HwcLayer *hwcLayer) +{ + uint32_t format = hwcLayer->getFormat(); + uint32_t trans = hwcLayer->getLayer()->transform; + + if (planeType == DisplayPlane::PLANE_SPRITE || planeType == DisplayPlane::PLANE_PRIMARY) { + switch (format) { + case HAL_PIXEL_FORMAT_BGRA_8888: + case HAL_PIXEL_FORMAT_BGRX_8888: + case HAL_PIXEL_FORMAT_RGBA_8888: + case HAL_PIXEL_FORMAT_RGBX_8888: + case HAL_PIXEL_FORMAT_RGB_565: + return trans ? false : true; + default: + VTRACE("unsupported format %#x", format); + return false; + } + } else if (planeType == DisplayPlane::PLANE_OVERLAY) { + switch (format) { + case HAL_PIXEL_FORMAT_I420: + case HAL_PIXEL_FORMAT_YUY2: + case HAL_PIXEL_FORMAT_UYVY: + // TODO: overlay supports 180 degree rotation + if (trans == HAL_TRANSFORM_ROT_180) { + WTRACE("180 degree rotation is not supported yet"); + } + return trans ? false : true; + case HAL_PIXEL_FORMAT_YV12: + return trans ? false: true; + case HAL_PIXEL_FORMAT_NV12: + case OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar: + case OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar_Tiled: + return true; + default: + VTRACE("unsupported format %#x", format); + return false; + } + } else { + ETRACE("invalid plane type %d", planeType); + return false; + } +} + +bool PlaneCapabilities::isSizeSupported(int planeType, HwcLayer *hwcLayer) +{ + uint32_t format = hwcLayer->getFormat(); + uint32_t w = hwcLayer->getBufferWidth(); + uint32_t h = hwcLayer->getBufferHeight(); + const stride_t& stride = hwcLayer->getBufferStride(); + + bool isYUVPacked; + uint32_t maxStride; + + if (planeType == DisplayPlane::PLANE_SPRITE || planeType == DisplayPlane::PLANE_PRIMARY) { + switch (format) { + case HAL_PIXEL_FORMAT_BGRA_8888: + case HAL_PIXEL_FORMAT_BGRX_8888: + case HAL_PIXEL_FORMAT_RGBA_8888: + case HAL_PIXEL_FORMAT_RGBX_8888: + case HAL_PIXEL_FORMAT_RGB_565: + if (stride.rgb.stride > SPRITE_PLANE_MAX_STRIDE_LINEAR) { + VTRACE("too large stride %d", stride.rgb.stride); + return false; + } + return true; + default: + VTRACE("unsupported format %#x", format); + return false; + } + } else if (planeType == DisplayPlane::PLANE_OVERLAY) { + switch (format) { + case HAL_PIXEL_FORMAT_YV12: + case HAL_PIXEL_FORMAT_I420: + case HAL_PIXEL_FORMAT_NV12: + case OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar: + case OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar_Tiled: + isYUVPacked = false; + break; + case HAL_PIXEL_FORMAT_YUY2: + case HAL_PIXEL_FORMAT_UYVY: + isYUVPacked = true; + break; + default: + VTRACE("unsupported format %#x", format); + return false; + } + // don't use overlay plane if stride is too big + maxStride = OVERLAY_PLANE_MAX_STRIDE_LINEAR; + if (isYUVPacked) { + maxStride = OVERLAY_PLANE_MAX_STRIDE_PACKED; + } + + if (stride.yuv.yStride > maxStride) { + VTRACE("stride %d is too large", stride.yuv.yStride); + return false; + } + return true; + } else { + ETRACE("invalid plane type %d", planeType); + return false; + } +} + +bool PlaneCapabilities::isBlendingSupported(int planeType, HwcLayer *hwcLayer) +{ + uint32_t blending = (uint32_t)hwcLayer->getLayer()->blending; + uint8_t planeAlpha = hwcLayer->getLayer()->planeAlpha; + + if (planeType == DisplayPlane::PLANE_SPRITE || planeType == DisplayPlane::PLANE_PRIMARY) { + bool ret = false; + + // support premultipled & none blanding + switch (blending) { + case HWC_BLENDING_NONE: + return true; + case HWC_BLENDING_PREMULT: + ret = false; + if ((planeAlpha == 0) || (planeAlpha == 255)) { + ret = true; + } + return ret; + default: + VTRACE("unsupported blending %#x", blending); + return false; + } + } else if (planeType == DisplayPlane::PLANE_OVERLAY) { + // overlay doesn't support blending + return (blending == HWC_BLENDING_NONE) ? true : false; + } else { + ETRACE("invalid plane type %d", planeType); + return false; + } +} + + +bool PlaneCapabilities::isScalingSupported(int planeType, HwcLayer *hwcLayer) +{ + hwc_frect_t& src = hwcLayer->getLayer()->sourceCropf; + hwc_rect_t& dest = hwcLayer->getLayer()->displayFrame; + + int srcW, srcH; + int dstW, dstH; + + srcW = (int)src.right - (int)src.left; + srcH = (int)src.bottom - (int)src.top; + dstW = dest.right - dest.left; + dstH = dest.bottom - dest.top; + + if (planeType == DisplayPlane::PLANE_SPRITE || planeType == DisplayPlane::PLANE_PRIMARY) { + // no scaling is supported + return ((srcW == dstW) && (srcH == dstH)) ? true : false; + + } else if (planeType == DisplayPlane::PLANE_OVERLAY) { + // overlay cannot support resolution that bigger than 2047x2047. + if ((srcW > INTEL_OVERLAY_MAX_WIDTH - 1) || (srcH > INTEL_OVERLAY_MAX_HEIGHT - 1)) { + return false; + } + + if (dstW <= 1 || dstH <= 1 || srcW <= 1 || srcH <= 1) { + // Workaround: Overlay flip when height is 1 causes MIPI stall on TNG + return false; + } + + return true; + } else if (planeType == DisplayPlane::PLANE_CURSOR) { + if (srcW > 256 || srcH > 256) { + return false; + } + return true; + } else { + ETRACE("invalid plane type %d", planeType); + return false; + } +} + +bool PlaneCapabilities::isTransformSupported(int planeType, HwcLayer *hwcLayer) +{ + uint32_t trans = hwcLayer->getLayer()->transform; + + if (planeType == DisplayPlane::PLANE_OVERLAY) { + // overlay does not support FLIP_H/FLIP_V + switch (trans) { + case 0: + case HAL_TRANSFORM_ROT_90: + case HAL_TRANSFORM_ROT_180: + case HAL_TRANSFORM_ROT_270: + return true; + default: + return false; + } + } + + // don't transform any tranform + return trans ? false : true; +} + +} // namespace intel +} // namespace android + diff --git a/merrifield/ips/common/PrepareListener.cpp b/merrifield/ips/common/PrepareListener.cpp new file mode 100644 index 0000000..0b4b05d --- /dev/null +++ b/merrifield/ips/common/PrepareListener.cpp @@ -0,0 +1,50 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <HwcTrace.h> +#include <Drm.h> +#include <Hwcomposer.h> +#include <common/PrepareListener.h> + +namespace android { +namespace intel { + +PrepareListener::PrepareListener() + : IPrepareListener() +{ +} + +PrepareListener::~PrepareListener() +{ +} + +void PrepareListener::onProtectedLayerStart(int disp) +{ + WTRACE("disp = %d, ignored for now", disp); + // need chaabi support for granular IED control + return; + + Drm *drm = Hwcomposer::getInstance().getDrm(); + int ret = drmCommandNone(drm->getDrmFd(), DRM_PSB_HDCP_DISPLAY_IED_ON); + if (ret != 0) { + ETRACE("failed to turn on display IED"); + } else { + ITRACE("display IED is turned on"); + } +} + +} // namespace intel +} // namespace android diff --git a/merrifield/ips/common/PrepareListener.h b/merrifield/ips/common/PrepareListener.h new file mode 100644 index 0000000..e048b92 --- /dev/null +++ b/merrifield/ips/common/PrepareListener.h @@ -0,0 +1,35 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef PREPARE_LISTENER_H +#define PREPARE_LISTENER_H + +#include <IPrepareListener.h> + +namespace android { +namespace intel { + +class PrepareListener : public IPrepareListener { +public: + PrepareListener(); + virtual ~PrepareListener(); +public: + virtual void onProtectedLayerStart(int disp); +}; + +} // namespace intel +} // namespace android + +#endif /* PREPARE_LISTENER_H */ diff --git a/merrifield/ips/common/RotationBufferProvider.cpp b/merrifield/ips/common/RotationBufferProvider.cpp new file mode 100644 index 0000000..65a4db8 --- /dev/null +++ b/merrifield/ips/common/RotationBufferProvider.cpp @@ -0,0 +1,637 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <HwcTrace.h> +#include <common/RotationBufferProvider.h> + +namespace android { +namespace intel { + +#define CHECK_VA_STATUS_RETURN(FUNC) \ +if (vaStatus != VA_STATUS_SUCCESS) {\ + ETRACE(FUNC" failed. vaStatus = %#x", vaStatus);\ + return false;\ +} + +#define CHECK_VA_STATUS_BREAK(FUNC) \ +if (vaStatus != VA_STATUS_SUCCESS) {\ + ETRACE(FUNC" failed. vaStatus = %#x", vaStatus);\ + break;\ +} + +// With this display value, VA will hook VED driver insead of VSP driver for buffer rotation +#define DISPLAYVALUE 0x56454450 + +RotationBufferProvider::RotationBufferProvider(Wsbm* wsbm) + : mWsbm(wsbm), + mVaInitialized(false), + mVaDpy(0), + mVaCfg(0), + mVaCtx(0), + mVaBufFilter(0), + mSourceSurface(0), + mDisplay(DISPLAYVALUE), + mWidth(0), + mHeight(0), + mTransform(0), + mRotatedWidth(0), + mRotatedHeight(0), + mRotatedStride(0), + mTargetIndex(0), + mTTMWrappers(), + mBobDeinterlace(0) +{ + for (int i = 0; i < MAX_SURFACE_NUM; i++) { + mKhandles[i] = 0; + mRotatedSurfaces[i] = 0; + mDrmBuf[i] = NULL; + } +} + +RotationBufferProvider::~RotationBufferProvider() +{ +} + +uint32_t RotationBufferProvider::getMilliseconds() +{ + struct timeval ptimeval; + gettimeofday(&ptimeval, NULL); + return (uint32_t)((ptimeval.tv_sec * 1000) + (ptimeval.tv_usec / 1000)); +} + +bool RotationBufferProvider::initialize() +{ + if (NULL == mWsbm) + return false; + mTTMWrappers.setCapacity(TTM_WRAPPER_COUNT); + return true; +} + +void RotationBufferProvider::deinitialize() +{ + stopVA(); + reset(); +} + +void RotationBufferProvider::reset() +{ + if (mTTMWrappers.size()) { + invalidateCaches(); + } +} + +void RotationBufferProvider::invalidateCaches() +{ + void *buf; + + for (size_t i = 0; i < mTTMWrappers.size(); i++) { + buf = mTTMWrappers.valueAt(i); + if (!mWsbm->destroyTTMBuffer(buf)) + WTRACE("failed to free TTMBuffer"); + } + mTTMWrappers.clear(); +} + +int RotationBufferProvider::transFromHalToVa(int transform) +{ + if (transform == HAL_TRANSFORM_ROT_90) + return VA_ROTATION_90; + if (transform == HAL_TRANSFORM_ROT_180) + return VA_ROTATION_180; + if (transform == HAL_TRANSFORM_ROT_270) + return VA_ROTATION_270; + return 0; +} + +int RotationBufferProvider::getStride(bool isTarget, int width) +{ + int stride = 0; + if (width <= 512) + stride = 512; + else if (width <= 1024) + stride = 1024; + else if (width <= 1280) { + stride = 1280; + if (isTarget) + stride = 2048; + } else if (width <= 2048) + stride = 2048; + else if (width <= 4096) + stride = 4096; + else + stride = (width + 0x3f) & ~0x3f; + return stride; +} + +buffer_handle_t RotationBufferProvider::createWsbmBuffer(int width, int height, void **buf) +{ + int size = width * height * 3 / 2; // YUV420 NV12 format + int allignment = 16 * 2048; // tiling row stride aligned + bool ret = mWsbm->allocateTTMBuffer(size, allignment, buf); + + if (ret == false) { + ETRACE("failed to allocate TTM buffer"); + return 0; + } + + return (buffer_handle_t) mWsbm->getKBufHandle(*buf); +} + +bool RotationBufferProvider::createVaSurface(VideoPayloadBuffer *payload, int transform, bool isTarget) +{ + VAStatus vaStatus; + VASurfaceAttributeTPI attribTpi; + VASurfaceAttributeTPI *vaSurfaceAttrib = &attribTpi; + int stride; + unsigned long buffers; + VASurfaceID *surface; + int width = 0, height = 0, bufferHeight = 0; + + if (isTarget) { + if (transFromHalToVa(transform) == VA_ROTATION_180) { + width = payload->width; + height = payload->height; + } else { + width = payload->height; + height = payload->width; + } + mRotatedWidth = width; + mRotatedHeight = height; + bufferHeight = (height + 0x1f) & ~0x1f; + stride = getStride(isTarget, width); + } else { + width = payload->width; + height = payload->height; + bufferHeight = (payload->height + 0x1f) & ~0x1f; + stride = payload->luma_stride; /* NV12 srouce buffer */ + } + + if (!stride) { + ETRACE("invalid stride value"); + return false; + } + + mBobDeinterlace = payload->bob_deinterlace; + // adjust source target for Bob deinterlace + if (!isTarget && mBobDeinterlace) { + height >>= 1; + bufferHeight >>= 1; + stride <<= 1; + } + + vaSurfaceAttrib->count = 1; + vaSurfaceAttrib->width = width; + vaSurfaceAttrib->height = height; + vaSurfaceAttrib->pixel_format = payload->format; + vaSurfaceAttrib->type = VAExternalMemoryKernelDRMBufffer; + vaSurfaceAttrib->tiling = payload->tiling; + vaSurfaceAttrib->size = (stride * bufferHeight * 3) / 2; + vaSurfaceAttrib->luma_offset = 0; + vaSurfaceAttrib->chroma_v_offset = stride * bufferHeight; + vaSurfaceAttrib->luma_stride = vaSurfaceAttrib->chroma_u_stride + = vaSurfaceAttrib->chroma_v_stride + = stride; + vaSurfaceAttrib->chroma_u_offset = vaSurfaceAttrib->chroma_v_offset; + vaSurfaceAttrib->buffers = &buffers; + + if (isTarget) { + buffer_handle_t khandle = createWsbmBuffer(stride, bufferHeight, &mDrmBuf[mTargetIndex]); + if (khandle == 0) { + ETRACE("failed to create buffer by wsbm"); + return false; + } + + mKhandles[mTargetIndex] = khandle; + vaSurfaceAttrib->buffers[0] = (uintptr_t) khandle; + mRotatedStride = stride; + surface = &mRotatedSurfaces[mTargetIndex]; + } else { + vaSurfaceAttrib->buffers[0] = (uintptr_t) payload->khandle; + surface = &mSourceSurface; + /* set src surface width/height to video crop size */ + if (payload->crop_width && payload->crop_height) { + width = payload->crop_width; + height = (payload->crop_height >> mBobDeinterlace); + } else { + VTRACE("Invalid cropping width or height"); + payload->crop_width = width; + payload->crop_height = height; + } + } + + vaStatus = vaCreateSurfacesWithAttribute(mVaDpy, + width, + height, + VA_RT_FORMAT_YUV420, + 1, + surface, + vaSurfaceAttrib); + if (vaStatus != VA_STATUS_SUCCESS) { + ETRACE("vaCreateSurfacesWithAttribute returns %d", vaStatus); + ETRACE("Attributes: target: %d, width: %d, height %d, bufferHeight %d, tiling %d", + isTarget, width, height, bufferHeight, payload->tiling); + *surface = 0; + return false; + } + + return true; +} + +bool RotationBufferProvider::startVA(VideoPayloadBuffer *payload, int transform) +{ + bool ret = true; + VAStatus vaStatus; + VAEntrypoint *entryPoint; + VAConfigAttrib attribDummy; + int numEntryPoints; + bool supportVideoProcessing = false; + int majorVer = 0, minorVer = 0; + + // VA will hold a copy of the param pointer, so local varialbe doesn't work + mVaDpy = vaGetDisplay(&mDisplay); + if (NULL == mVaDpy) { + ETRACE("failed to get VADisplay"); + return false; + } + + vaStatus = vaInitialize(mVaDpy, &majorVer, &minorVer); + CHECK_VA_STATUS_RETURN("vaInitialize"); + + numEntryPoints = vaMaxNumEntrypoints(mVaDpy); + + if (numEntryPoints <= 0) { + ETRACE("numEntryPoints value is invalid"); + return false; + } + + entryPoint = (VAEntrypoint*)malloc(sizeof(VAEntrypoint) * numEntryPoints); + if (NULL == entryPoint) { + ETRACE("failed to malloc memory for entryPoint"); + return false; + } + + vaStatus = vaQueryConfigEntrypoints(mVaDpy, + VAProfileNone, + entryPoint, + &numEntryPoints); + CHECK_VA_STATUS_RETURN("vaQueryConfigEntrypoints"); + + for (int i = 0; i < numEntryPoints; i++) + if (entryPoint[i] == VAEntrypointVideoProc) + supportVideoProcessing = true; + + free(entryPoint); + entryPoint = NULL; + + if (!supportVideoProcessing) { + ETRACE("VAEntrypointVideoProc is not supported"); + return false; + } + + vaStatus = vaCreateConfig(mVaDpy, + VAProfileNone, + VAEntrypointVideoProc, + &attribDummy, + 0, + &mVaCfg); + CHECK_VA_STATUS_RETURN("vaCreateConfig"); + + // create first target surface + ret = createVaSurface(payload, transform, true); + if (ret == false) { + ETRACE("failed to create target surface with attribute"); + return false; + } + + vaStatus = vaCreateContext(mVaDpy, + mVaCfg, + payload->width, + payload->height, + 0, + &mRotatedSurfaces[0], + 1, + &mVaCtx); + CHECK_VA_STATUS_RETURN("vaCreateContext"); + + VAProcFilterType filters[VAProcFilterCount]; + unsigned int numFilters = VAProcFilterCount; + vaStatus = vaQueryVideoProcFilters(mVaDpy, mVaCtx, filters, &numFilters); + CHECK_VA_STATUS_RETURN("vaQueryVideoProcFilters"); + + bool supportVideoProcFilter = false; + for (unsigned int j = 0; j < numFilters; j++) + if (filters[j] == VAProcFilterNone) + supportVideoProcFilter = true; + + if (!supportVideoProcFilter) { + ETRACE("VAProcFilterNone is not supported"); + return false; + } + + VAProcFilterParameterBuffer filter; + filter.type = VAProcFilterNone; + filter.value = 0; + + vaStatus = vaCreateBuffer(mVaDpy, + mVaCtx, + VAProcFilterParameterBufferType, + sizeof(filter), + 1, + &filter, + &mVaBufFilter); + CHECK_VA_STATUS_RETURN("vaCreateBuffer"); + + VAProcPipelineCaps pipelineCaps; + unsigned int numCaps = 1; + vaStatus = vaQueryVideoProcPipelineCaps(mVaDpy, + mVaCtx, + &mVaBufFilter, + numCaps, + &pipelineCaps); + CHECK_VA_STATUS_RETURN("vaQueryVideoProcPipelineCaps"); + + if (!(pipelineCaps.rotation_flags & (1 << transFromHalToVa(transform)))) { + ETRACE("VA_ROTATION_xxx: 0x%08x is not supported by the filter", + transFromHalToVa(transform)); + return false; + } + + mVaInitialized = true; + + return true; +} + +bool RotationBufferProvider::setupRotationBuffer(VideoPayloadBuffer *payload, int transform) +{ +#ifdef DEBUG_ROTATION_PERFROMANCE + uint32_t setup_Begin = getMilliseconds(); +#endif + VAStatus vaStatus; + int stride; + bool ret = false; + + if (payload->format != VA_FOURCC_NV12 || payload->width == 0 || payload->height == 0) { + WTRACE("payload data is not correct: format %#x, width %d, height %d", + payload->format, payload->width, payload->height); + return ret; + } + + if (payload->width > 1280 && payload->width <= 2048) { + payload->tiling = 1; + } + + do { + if (isContextChanged(payload->width, payload->height, transform)) { + DTRACE("VA is restarted as rotation context changes"); + + if (mVaInitialized) { + stopVA(); // need to re-initialize VA for new rotation config + } + mTransform = transform; + mWidth = payload->width; + mHeight = payload->height; + } + + if (!mVaInitialized) { + ret = startVA(payload, transform); + if (ret == false) { + vaStatus = VA_STATUS_ERROR_OPERATION_FAILED; + break; + } + } + + // start to create next target surface + if (!mRotatedSurfaces[mTargetIndex]) { + ret = createVaSurface(payload, transform, true); + if (ret == false) { + ETRACE("failed to create target surface with attribute"); + vaStatus = VA_STATUS_ERROR_OPERATION_FAILED; + break; + } + } + + // create source surface + ret = createVaSurface(payload, transform, false); + if (ret == false) { + ETRACE("failed to create source surface with attribute"); + vaStatus = VA_STATUS_ERROR_OPERATION_FAILED; + break; + } + +#ifdef DEBUG_ROTATION_PERFROMANCE + uint32_t beginPicture = getMilliseconds(); +#endif + vaStatus = vaBeginPicture(mVaDpy, mVaCtx, mRotatedSurfaces[mTargetIndex]); + CHECK_VA_STATUS_BREAK("vaBeginPicture"); + + VABufferID pipelineBuf; + void *p; + VAProcPipelineParameterBuffer *pipelineParam; + vaStatus = vaCreateBuffer(mVaDpy, + mVaCtx, + VAProcPipelineParameterBufferType, + sizeof(*pipelineParam), + 1, + NULL, + &pipelineBuf); + CHECK_VA_STATUS_BREAK("vaCreateBuffer"); + + vaStatus = vaMapBuffer(mVaDpy, pipelineBuf, &p); + CHECK_VA_STATUS_BREAK("vaMapBuffer"); + + pipelineParam = (VAProcPipelineParameterBuffer*)p; + pipelineParam->surface = mSourceSurface; + pipelineParam->rotation_state = transFromHalToVa(transform); + pipelineParam->filters = &mVaBufFilter; + pipelineParam->num_filters = 1; + pipelineParam->surface_region = NULL; + pipelineParam->output_region = NULL; + pipelineParam->num_forward_references = 0; + pipelineParam->num_backward_references = 0; + vaStatus = vaUnmapBuffer(mVaDpy, pipelineBuf); + CHECK_VA_STATUS_BREAK("vaUnmapBuffer"); + + vaStatus = vaRenderPicture(mVaDpy, mVaCtx, &pipelineBuf, 1); + CHECK_VA_STATUS_BREAK("vaRenderPicture"); + + vaStatus = vaEndPicture(mVaDpy, mVaCtx); + CHECK_VA_STATUS_BREAK("vaEndPicture"); + + vaStatus = vaSyncSurface(mVaDpy, mRotatedSurfaces[mTargetIndex]); + CHECK_VA_STATUS_BREAK("vaSyncSurface"); + +#ifdef DEBUG_ROTATION_PERFROMANCE + ITRACE("time spent %dms from vaBeginPicture to vaSyncSurface", + getMilliseconds() - beginPicture); +#endif + + // Populate payload fields so that overlayPlane can flip the buffer + payload->rotated_width = mRotatedStride; + payload->rotated_height = mRotatedHeight; + payload->rotated_buffer_handle = mKhandles[mTargetIndex]; + // setting client transform to 0 to force re-generating rotated buffer whenever needed. + payload->client_transform = 0; + mTargetIndex++; + if (mTargetIndex >= MAX_SURFACE_NUM) + mTargetIndex = 0; + + } while (0); + +#ifdef DEBUG_ROTATION_PERFROMANCE + ITRACE("time spent %dms for setupRotationBuffer", + getMilliseconds() - setup_Begin); +#endif + + if (mSourceSurface > 0) { + vaStatus = vaDestroySurfaces(mVaDpy, &mSourceSurface, 1); + if (vaStatus != VA_STATUS_SUCCESS) + WTRACE("vaDestroySurfaces failed, vaStatus = %d", vaStatus); + mSourceSurface = 0; + } + + if (vaStatus != VA_STATUS_SUCCESS) { + stopVA(); + return false; // To not block HWC, just abort instead of retry + } + + if (!payload->khandle) { + WTRACE("khandle is reset by decoder, surface is invalid!"); + return false; + } + + return true; +} + +bool RotationBufferProvider::prepareBufferInfo(int w, int h, int stride, VideoPayloadBuffer *payload, void *user_pt) +{ + int chroma_offset, size; + void *buf = NULL; + + payload->width = payload->crop_width = w; + payload->height = payload->crop_height = h; + payload->coded_width = ((w + 0xf) & ~0xf); + payload->coded_height = ((h + 0xf) & ~0xf); + payload->format = VA_FOURCC_NV12; + payload->tiling = 1; + payload->luma_stride = stride; + payload->chroma_u_stride = stride; + payload->chroma_v_stride = stride; + payload->client_transform = 0; + payload->bob_deinterlace = 0; + + chroma_offset = stride * h; + size = stride * h + stride * h / 2; + + ssize_t index; + index = mTTMWrappers.indexOfKey((uint64_t)user_pt); + if (index < 0) { + VTRACE("wrapped userPt as wsbm buffer"); + bool ret = mWsbm->allocateTTMBufferUB(size, 0, &buf, user_pt); + if (ret == false) { + ETRACE("failed to allocate TTM buffer"); + return ret; + } + + if (mTTMWrappers.size() >= TTM_WRAPPER_COUNT) { + WTRACE("mTTMWrappers is unexpectedly full. Invalidate caches"); + invalidateCaches(); + } + + index = mTTMWrappers.add((uint64_t)user_pt, buf); + } else { + VTRACE("got wsbmBuffer in saved caches"); + buf = mTTMWrappers.valueAt(index); + } + + payload->khandle = (buffer_handle_t) mWsbm->getKBufHandle(buf); + return true; +} + +void RotationBufferProvider::freeVaSurfaces() +{ + bool ret; + VAStatus vaStatus; + + for (int i = 0; i < MAX_SURFACE_NUM; i++) { + if (NULL != mDrmBuf[i]) { + ret = mWsbm->destroyTTMBuffer(mDrmBuf[i]); + if (!ret) + WTRACE("failed to free TTMBuffer"); + mDrmBuf[i] = NULL; + } + } + + // remove wsbm buffer ref from VA + for (int j = 0; j < MAX_SURFACE_NUM; j++) { + if (0 != mRotatedSurfaces[j]) { + vaStatus = vaDestroySurfaces(mVaDpy, &mRotatedSurfaces[j], 1); + if (vaStatus != VA_STATUS_SUCCESS) + WTRACE("vaDestroySurfaces failed, vaStatus = %d", vaStatus); + } + mRotatedSurfaces[j] = 0; + } +} + +void RotationBufferProvider::stopVA() +{ + freeVaSurfaces(); + + if (0 != mVaBufFilter) + vaDestroyBuffer(mVaDpy, mVaBufFilter); + if (0 != mVaCfg) + vaDestroyConfig(mVaDpy,mVaCfg); + if (0 != mVaCtx) + vaDestroyContext(mVaDpy, mVaCtx); + if (0 != mVaDpy) + vaTerminate(mVaDpy); + + mVaInitialized = false; + + for (int i = 0; i < MAX_SURFACE_NUM; i++) { + mKhandles[i] = 0; + mRotatedSurfaces[i] = 0; + mDrmBuf[i] = NULL; + } + // reset VA variable + mVaDpy = 0; + mVaCfg = 0; + mVaCtx = 0; + mVaBufFilter = 0; + mSourceSurface = 0; + + mWidth = 0; + mHeight = 0; + mRotatedWidth = 0; + mRotatedHeight = 0; + mRotatedStride = 0; + mTargetIndex = 0; + mBobDeinterlace = 0; +} + +bool RotationBufferProvider::isContextChanged(int width, int height, int transform) +{ + // check rotation config + if (height == mHeight && + width == mWidth && + transform == mTransform) { + return false; + } + + return true; +} + +} // name space intel +} // name space android diff --git a/merrifield/ips/common/RotationBufferProvider.h b/merrifield/ips/common/RotationBufferProvider.h new file mode 100644 index 0000000..edf4fa7 --- /dev/null +++ b/merrifield/ips/common/RotationBufferProvider.h @@ -0,0 +1,102 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef __ROTATIONO_BUFFER_PROVIDER_H__ +#define __ROTATIONO_BUFFER_PROVIDER_H__ + +#include <va/va.h> +#include <sys/time.h> +#include <va/va_tpi.h> +#include <va/va_vpp.h> +#include <common/Wsbm.h> +#include <utils/Timers.h> +#include <utils/KeyedVector.h> +#include <va/va_android.h> +#include <common/VideoPayloadBuffer.h> + +namespace android { +namespace intel { + +#define Display unsigned int +typedef void* VADisplay; +typedef int VAStatus; + +class RotationBufferProvider { + +public: + RotationBufferProvider(Wsbm* wsbm); + ~RotationBufferProvider(); + + bool initialize(); + void deinitialize(); + void reset(); + bool setupRotationBuffer(VideoPayloadBuffer *payload, int transform); + bool prepareBufferInfo(int, int, int, VideoPayloadBuffer *, void *); + +private: + void invalidateCaches(); + bool startVA(VideoPayloadBuffer *payload, int transform); + void stopVA(); + bool isContextChanged(int width, int height, int transform); + int transFromHalToVa(int transform); + buffer_handle_t createWsbmBuffer(int width, int height, void **buf); + int getStride(bool isTarget, int width); + bool createVaSurface(VideoPayloadBuffer *payload, int transform, bool isTarget); + void freeVaSurfaces(); + inline uint32_t getMilliseconds(); + +private: + enum { + MAX_SURFACE_NUM = 4 + }; + + Wsbm* mWsbm; + + bool mVaInitialized; + VADisplay mVaDpy; + VAConfigID mVaCfg; + VAContextID mVaCtx; + VABufferID mVaBufFilter; + VASurfaceID mSourceSurface; + Display mDisplay; + + // rotation config variables + int mWidth; + int mHeight; + int mTransform; + + int mRotatedWidth; + int mRotatedHeight; + int mRotatedStride; + + int mTargetIndex; + buffer_handle_t mKhandles[MAX_SURFACE_NUM]; + VASurfaceID mRotatedSurfaces[MAX_SURFACE_NUM]; + void *mDrmBuf[MAX_SURFACE_NUM]; + + enum { + TTM_WRAPPER_COUNT = 10, + }; + + KeyedVector<uint64_t, void*> mTTMWrappers; /* userPt/wsbmBuffer */ + + int mBobDeinterlace; +}; + +} // name space intel +} // name space android + +#endif diff --git a/merrifield/ips/common/SpritePlaneBase.cpp b/merrifield/ips/common/SpritePlaneBase.cpp new file mode 100644 index 0000000..890aec8 --- /dev/null +++ b/merrifield/ips/common/SpritePlaneBase.cpp @@ -0,0 +1,53 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <HwcTrace.h> +#include <common/SpritePlaneBase.h> +#include <common/PixelFormat.h> + +namespace android { +namespace intel { + +SpritePlaneBase::SpritePlaneBase(int index, int disp) + : DisplayPlane(index, PLANE_SPRITE, disp), + mForceBottom(false), + mAbovePrimary(true) +{ + CTRACE(); +} + +SpritePlaneBase::~SpritePlaneBase() +{ + CTRACE(); +} + +bool SpritePlaneBase::flip(void *ctx) +{ + CTRACE(); + return DisplayPlane::flip(ctx); +} + +bool SpritePlaneBase::enable() +{ + return enablePlane(true); +} + +bool SpritePlaneBase::disable() +{ + return enablePlane(false); +} + +} // namespace intel +} // namespace android diff --git a/merrifield/ips/common/SpritePlaneBase.h b/merrifield/ips/common/SpritePlaneBase.h new file mode 100644 index 0000000..78bbd6e --- /dev/null +++ b/merrifield/ips/common/SpritePlaneBase.h @@ -0,0 +1,52 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef SPRITE_PLANE_BASE_H +#define SPRITE_PLANE_BASE_H + +#include <utils/KeyedVector.h> +#include <hal_public.h> +#include <BufferCache.h> +#include <DisplayPlane.h> + +namespace android { +namespace intel { + +class SpritePlaneBase : public DisplayPlane { +public: + SpritePlaneBase(int index, int disp); + virtual ~SpritePlaneBase(); +public: + // hardware operations + virtual bool flip(void *ctx); + virtual bool enable(); + virtual bool disable(); + virtual bool isDisabled() = 0; + + // display device + virtual void* getContext() const = 0; +protected: + virtual bool setDataBuffer(BufferMapper& mapper) = 0; + virtual bool enablePlane(bool enabled) = 0; +protected: + bool mForceBottom; + bool mAbovePrimary; +}; + +} // namespace intel +} // namespace android + +#endif /* SPRITE_PLANE_BASE_H */ + diff --git a/merrifield/ips/common/TTMBufferMapper.cpp b/merrifield/ips/common/TTMBufferMapper.cpp new file mode 100644 index 0000000..7c3ed0d --- /dev/null +++ b/merrifield/ips/common/TTMBufferMapper.cpp @@ -0,0 +1,103 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <HwcTrace.h> +#include <common/TTMBufferMapper.h> + +namespace android { +namespace intel { + +TTMBufferMapper::TTMBufferMapper(Wsbm& wsbm, DataBuffer& buffer) + : BufferMapper(buffer), + mRefCount(0), + mWsbm(wsbm), + mBufferObject(0), + mGttOffsetInPage(0), + mCpuAddress(0), + mSize(0) +{ + CTRACE(); +} + +TTMBufferMapper::~TTMBufferMapper() +{ + CTRACE(); +} + +bool TTMBufferMapper::map() +{ + void *wsbmBufferObject = 0; + buffer_handle_t handle; + void *virtAddr; + uint32_t gttOffsetInPage; + + CTRACE(); + + handle = getHandle(); + + bool ret = mWsbm.wrapTTMBuffer((int64_t)handle, &wsbmBufferObject); + if (ret == false) { + ETRACE("failed to map TTM buffer"); + return false; + } + + // TODO: review this later + ret = mWsbm.waitIdleTTMBuffer(wsbmBufferObject); + if (ret == false) { + ETRACE("failed to wait ttm buffer idle"); + return false; + } + + virtAddr = mWsbm.getCPUAddress(wsbmBufferObject); + gttOffsetInPage = mWsbm.getGttOffset(wsbmBufferObject); + + if (!gttOffsetInPage || !virtAddr) { + WTRACE("offset = %#x, addr = %p.", gttOffsetInPage, virtAddr); + return false; + } + + // update parameters + mBufferObject = wsbmBufferObject; + mGttOffsetInPage = gttOffsetInPage; + mCpuAddress = virtAddr; + mSize = 0; + return true; +} + +bool TTMBufferMapper::unmap() +{ + CTRACE(); + + if (!mBufferObject) + return false; + + mWsbm.unreferenceTTMBuffer(mBufferObject); + + mGttOffsetInPage = 0; + mCpuAddress = 0; + mSize = 0; + mBufferObject = 0; + return true; +} + +bool TTMBufferMapper::waitIdle() +{ + return mWsbm.waitIdleTTMBuffer(mBufferObject); +} + +} // namespace intel +} // namespace android + + diff --git a/merrifield/ips/common/TTMBufferMapper.h b/merrifield/ips/common/TTMBufferMapper.h new file mode 100644 index 0000000..46ed26e --- /dev/null +++ b/merrifield/ips/common/TTMBufferMapper.h @@ -0,0 +1,70 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef TTMBUFFERMAPPER_H_ +#define TTMBUFFERMAPPER_H_ + +#include <DataBuffer.h> +#include <BufferMapper.h> +#include <common/Wsbm.h> + +namespace android { +namespace intel { + +class TTMBufferMapper : public BufferMapper { +public: + TTMBufferMapper(Wsbm& wsbm, DataBuffer& buffer); + virtual ~TTMBufferMapper(); +public: + bool map(); + bool unmap(); + + uint32_t getGttOffsetInPage(int subIndex) const { + return mGttOffsetInPage; + } + void* getCpuAddress(int subIndex) const { + return mCpuAddress; + } + uint32_t getSize(int subIndex) const { + return mSize; + } + buffer_handle_t getKHandle(int subIndex) { + return 0; + } + buffer_handle_t getFbHandle(int subIndex) { + return 0; + } + void putFbHandle() { + return; + } + + // wait idle + bool waitIdle(); +private: + int mRefCount; + Wsbm& mWsbm; + void* mBufferObject; + + // mapped info + uint32_t mGttOffsetInPage; + void* mCpuAddress; + uint32_t mSize; +}; + +} //namespace intel +} //namespace android + + +#endif /* TTMBUFFERMAPPER_H_ */ diff --git a/merrifield/ips/common/VideoPayloadBuffer.h b/merrifield/ips/common/VideoPayloadBuffer.h new file mode 100644 index 0000000..1faf84a --- /dev/null +++ b/merrifield/ips/common/VideoPayloadBuffer.h @@ -0,0 +1,87 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef VIDEO_PAYLOAD_BUFFER_H +#define VIDEO_PAYLOAD_BUFFER_H + +#include <utils/Timers.h> +namespace android { +namespace intel { + +struct VideoPayloadBuffer { + // transform made by clients (clients to hwc) + int client_transform; + int metadata_transform; + int rotated_width; + int rotated_height; + int surface_protected; + int force_output_method; + buffer_handle_t rotated_buffer_handle; + uint32_t renderStatus; + unsigned int used_by_widi; + int bob_deinterlace; + int tiling; + uint32_t width; + uint32_t height; + uint32_t luma_stride; + uint32_t chroma_u_stride; + uint32_t chroma_v_stride; + uint32_t format; + buffer_handle_t khandle; + int64_t timestamp; + + uint32_t rotate_luma_stride; + uint32_t rotate_chroma_u_stride; + uint32_t rotate_chroma_v_stride; + + nsecs_t hwc_timestamp; + uint32_t layer_transform; + + void *native_window; + buffer_handle_t scaling_khandle; + uint32_t scaling_width; + uint32_t scaling_height; + + uint32_t scaling_luma_stride; + uint32_t scaling_chroma_u_stride; + uint32_t scaling_chroma_v_stride; + + uint32_t crop_width; + uint32_t crop_height; + + uint32_t coded_width; + uint32_t coded_height; + uint32_t csc_mode; + uint32_t video_range; + uint32_t initialized; +}; + + +// force output method values +enum { + FORCE_OUTPUT_INVALID = 0, + FORCE_OUTPUT_GPU, + FORCE_OUTPUT_OVERLAY, + FORCE_OUTPUT_SW_DECODE, +}; + + +} // namespace intel +} // namespace android + + +#endif // VIDEO_PAYLOAD_BUFFER_H + + diff --git a/merrifield/ips/common/VideoPayloadManager.cpp b/merrifield/ips/common/VideoPayloadManager.cpp new file mode 100644 index 0000000..33e2afb --- /dev/null +++ b/merrifield/ips/common/VideoPayloadManager.cpp @@ -0,0 +1,123 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <HwcTrace.h> +#include <BufferMapper.h> +#include <common/GrallocSubBuffer.h> +#include <common/VideoPayloadManager.h> +#include <common/VideoPayloadBuffer.h> + +namespace android { +namespace intel { + +VideoPayloadManager::VideoPayloadManager() + : IVideoPayloadManager() +{ +} + +VideoPayloadManager::~VideoPayloadManager() +{ +} + +bool VideoPayloadManager::getMetaData(BufferMapper *mapper, MetaData *metadata) +{ + if (!mapper || !metadata) { + ETRACE("Null input params"); + return false; + } + + VideoPayloadBuffer *p = (VideoPayloadBuffer*) mapper->getCpuAddress(SUB_BUFFER1); + if (!p) { + ETRACE("Got null payload from display buffer"); + return false; + } + + metadata->format = p->format; + metadata->transform = p->metadata_transform; + metadata->timestamp = p->timestamp; + + metadata->normalBuffer.khandle = p->khandle; + metadata->normalBuffer.width = p->crop_width; + metadata->normalBuffer.height = p->crop_height; + metadata->normalBuffer.bufWidth = p->width; + metadata->normalBuffer.bufHeight = p->height; + metadata->normalBuffer.lumaStride = p->luma_stride; + metadata->normalBuffer.chromaUStride = p->chroma_u_stride; + metadata->normalBuffer.chromaVStride = p->chroma_v_stride; + metadata->normalBuffer.offsetX = 0; + metadata->normalBuffer.offsetY = 0; + metadata->normalBuffer.tiled = (p->width > 1280); + + metadata->scalingBuffer.khandle = p->scaling_khandle; + metadata->scalingBuffer.width = p->scaling_width; + metadata->scalingBuffer.height = p->scaling_height; + metadata->scalingBuffer.bufWidth = align_to(p->scaling_width, 32); + metadata->scalingBuffer.bufHeight = align_to(p->scaling_height, 32); + metadata->scalingBuffer.lumaStride = p->scaling_luma_stride; + metadata->scalingBuffer.chromaUStride = p->scaling_chroma_u_stride; + metadata->scalingBuffer.chromaVStride = p->scaling_chroma_v_stride; + metadata->scalingBuffer.offsetX = 0; + metadata->scalingBuffer.offsetY = 0; + metadata->scalingBuffer.tiled = false; + + metadata->rotationBuffer.khandle = p->rotated_buffer_handle; + uint16_t rotSrcWidth; + uint16_t rotSrcHeight; + if (metadata->scalingBuffer.khandle) { + rotSrcWidth = metadata->scalingBuffer.width; + rotSrcHeight = metadata->scalingBuffer.height; + } else { + rotSrcWidth = metadata->normalBuffer.width; + rotSrcHeight = metadata->normalBuffer.height; + } + if (metadata->transform == 0 || metadata->transform == HAL_TRANSFORM_ROT_180) { + metadata->rotationBuffer.width = rotSrcWidth; + metadata->rotationBuffer.height = rotSrcHeight; + } else { + metadata->rotationBuffer.width = rotSrcHeight; + metadata->rotationBuffer.height = rotSrcWidth; + } + metadata->rotationBuffer.bufWidth = p->rotated_width; + metadata->rotationBuffer.bufHeight = p->rotated_height; + metadata->rotationBuffer.lumaStride = p->rotate_luma_stride; + metadata->rotationBuffer.chromaUStride = p->rotate_chroma_u_stride; + metadata->rotationBuffer.chromaVStride = p->rotate_chroma_v_stride; + metadata->rotationBuffer.offsetX = (-metadata->rotationBuffer.width) & 0xf; + metadata->rotationBuffer.offsetY = (-metadata->rotationBuffer.height) & 0xf; + metadata->rotationBuffer.tiled = metadata->normalBuffer.tiled; + + return true; +} + +bool VideoPayloadManager::setRenderStatus(BufferMapper *mapper, bool renderStatus) +{ + if (!mapper) { + ETRACE("Null mapper param"); + return false; + } + + VideoPayloadBuffer* p = (VideoPayloadBuffer*) mapper->getCpuAddress(SUB_BUFFER1); + if (!p) { + ETRACE("Got null payload from display buffer"); + return false; + } + + p->renderStatus = renderStatus ? 1 : 0; + return true; +} + +} // namespace intel +} // namespace android diff --git a/merrifield/ips/common/VideoPayloadManager.h b/merrifield/ips/common/VideoPayloadManager.h new file mode 100644 index 0000000..563003d --- /dev/null +++ b/merrifield/ips/common/VideoPayloadManager.h @@ -0,0 +1,42 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef VIDEO_PAYLOAD_MANAGER_H +#define VIDEO_PAYLOAD_MANAGER_H + +#include <IVideoPayloadManager.h> + +namespace android { +namespace intel { + +class BufferMapper; + +class VideoPayloadManager : public IVideoPayloadManager { + +public: + VideoPayloadManager(); + virtual ~VideoPayloadManager(); + + // IVideoPayloadManager +public: + virtual bool getMetaData(BufferMapper *mapper, MetaData *metadata); + virtual bool setRenderStatus(BufferMapper *mapper, bool renderStatus); + +}; // class VideoPayloadManager + +} // namespace intel +} // namespace android + +#endif /* VIDEO_PAYLOAD_MANAGER_H */ diff --git a/merrifield/ips/common/VsyncControl.cpp b/merrifield/ips/common/VsyncControl.cpp new file mode 100644 index 0000000..9e576fd --- /dev/null +++ b/merrifield/ips/common/VsyncControl.cpp @@ -0,0 +1,84 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <HwcTrace.h> +#include <Drm.h> +#include <Hwcomposer.h> +#include <common/VsyncControl.h> + +namespace android { +namespace intel { + +VsyncControl::VsyncControl() + : IVsyncControl(), + mInitialized(false) +{ +} + +VsyncControl::~VsyncControl() +{ + WARN_IF_NOT_DEINIT(); +} + +bool VsyncControl::initialize() +{ + mInitialized = true; + return true; +} + +void VsyncControl::deinitialize() +{ + mInitialized = false; +} + +bool VsyncControl::control(int disp, bool enabled) +{ + ATRACE("disp = %d, enabled = %d", disp, enabled); + + struct drm_psb_vsync_set_arg arg; + memset(&arg, 0, sizeof(struct drm_psb_vsync_set_arg)); + + // pipe equals to disp + arg.vsync.pipe = disp; + + if (enabled) { + arg.vsync_operation_mask = VSYNC_ENABLE; + } else { + arg.vsync_operation_mask = VSYNC_DISABLE; + } + Drm *drm = Hwcomposer::getInstance().getDrm(); + return drm->writeReadIoctl(DRM_PSB_VSYNC_SET, &arg, sizeof(arg)); +} + +bool VsyncControl::wait(int disp, int64_t& timestamp) +{ + ATRACE("disp = %d", disp); + + struct drm_psb_vsync_set_arg arg; + memset(&arg, 0, sizeof(struct drm_psb_vsync_set_arg)); + + arg.vsync_operation_mask = VSYNC_WAIT; + + // pipe equals to disp + arg.vsync.pipe = disp; + + Drm *drm = Hwcomposer::getInstance().getDrm(); + bool ret = drm->writeReadIoctl(DRM_PSB_VSYNC_SET, &arg, sizeof(arg)); + timestamp = (int64_t)arg.vsync.timestamp; + return ret; +} + +} // namespace intel +} // namespace android diff --git a/merrifield/ips/common/VsyncControl.h b/merrifield/ips/common/VsyncControl.h new file mode 100644 index 0000000..d5ffa11 --- /dev/null +++ b/merrifield/ips/common/VsyncControl.h @@ -0,0 +1,43 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef VSYNC_CONTROL_H +#define VSYNC_CONTROL_H + +#include <IVsyncControl.h> + +namespace android { +namespace intel { + +class VsyncControl : public IVsyncControl { +public: + VsyncControl(); + virtual ~VsyncControl(); + +public: + bool initialize(); + void deinitialize(); + bool control(int disp, bool enabled); + bool wait(int disp, int64_t& timestamp); + +private: + bool mInitialized; +}; + +} // namespace intel +} // namespace android + + +#endif /* VSYNC_CONTROL_H */ diff --git a/merrifield/ips/common/Wsbm.cpp b/merrifield/ips/common/Wsbm.cpp new file mode 100644 index 0000000..ab92b1b --- /dev/null +++ b/merrifield/ips/common/Wsbm.cpp @@ -0,0 +1,136 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <HwcTrace.h> +#include <common/Wsbm.h> + +Wsbm::Wsbm(int drmFD) + : mInitialized(false) +{ + CTRACE(); + mDrmFD = drmFD; +} + +Wsbm::~Wsbm() +{ + WARN_IF_NOT_DEINIT(); +} + +bool Wsbm::initialize() +{ + if (mInitialized) { + WTRACE("object is initialized"); + return true; + } + + int ret = psbWsbmInitialize(mDrmFD); + if (ret) { + ETRACE("failed to initialize Wsbm"); + return false; + } + + mInitialized = true; + return true; +} + +void Wsbm::deinitialize() +{ + if (!mInitialized) { + return; + } + psbWsbmTakedown(); + mInitialized = false; +} + +bool Wsbm::allocateTTMBuffer(uint32_t size, uint32_t align, void ** buf) +{ + int ret = psbWsbmAllocateTTMBuffer(size, align, buf); + if (ret) { + ETRACE("failed to allocate buffer"); + return false; + } + + return true; +} + +bool Wsbm::allocateTTMBufferUB(uint32_t size, uint32_t align, void ** buf, void *user_pt) +{ + int ret = psbWsbmAllocateFromUB(size, align, buf, user_pt); + if (ret) { + ETRACE("failed to allocate UB buffer"); + return false; + } + + return true; +} + +bool Wsbm::destroyTTMBuffer(void * buf) +{ + int ret = psbWsbmDestroyTTMBuffer(buf); + if (ret) { + ETRACE("failed to destroy buffer"); + return false; + } + + return true; +} + +void * Wsbm::getCPUAddress(void * buf) +{ + return psbWsbmGetCPUAddress(buf); +} + +uint32_t Wsbm::getGttOffset(void * buf) +{ + return psbWsbmGetGttOffset(buf); +} + +bool Wsbm::wrapTTMBuffer(int64_t handle, void **buf) +{ + int ret = psbWsbmWrapTTMBuffer(handle, buf); + if (ret) { + ETRACE("failed to wrap buffer"); + return false; + } + + return true; +} + +bool Wsbm::unreferenceTTMBuffer(void *buf) +{ + int ret = psbWsbmUnReference(buf); + if (ret) { + ETRACE("failed to unreference buffer"); + return false; + } + + return true; +} + +uint64_t Wsbm::getKBufHandle(void *buf) +{ + return psbWsbmGetKBufHandle(buf); +} + +bool Wsbm::waitIdleTTMBuffer(void *buf) +{ + int ret = psbWsbmWaitIdle(buf); + if (ret) { + ETRACE("failed to wait ttm buffer for idle"); + return false; + } + + return true; +} diff --git a/merrifield/ips/common/Wsbm.h b/merrifield/ips/common/Wsbm.h new file mode 100644 index 0000000..9e2b357 --- /dev/null +++ b/merrifield/ips/common/Wsbm.h @@ -0,0 +1,47 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef WSBM_H__ +#define WSBM_H__ + +#include <common/WsbmWrapper.h> + +/** + * Class: WSBM + * A wrapper class to use libwsbm functionalities + */ +class Wsbm +{ +private: + int mDrmFD; +public: + Wsbm(int drmFD); + ~Wsbm(); + bool initialize(); + void deinitialize(); + bool allocateTTMBuffer(uint32_t size, uint32_t align,void ** buf); + bool allocateTTMBufferUB(uint32_t size, uint32_t align, void ** buf, void *user_pt); + bool destroyTTMBuffer(void * buf); + void * getCPUAddress(void * buf); + uint32_t getGttOffset(void * buf); + bool wrapTTMBuffer(int64_t handle, void **buf); + bool unreferenceTTMBuffer(void *buf); + bool waitIdleTTMBuffer(void *buf); + uint64_t getKBufHandle(void *buf); +private: + bool mInitialized; +}; + +#endif /*__INTEL_WSBM_H__*/ diff --git a/merrifield/ips/common/WsbmWrapper.c b/merrifield/ips/common/WsbmWrapper.c new file mode 100644 index 0000000..5e4161e --- /dev/null +++ b/merrifield/ips/common/WsbmWrapper.c @@ -0,0 +1,401 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <wsbm_pool.h> +#include <wsbm_driver.h> +#include <wsbm_manager.h> +#include <wsbm_util.h> +#include <drm/ttm/ttm_placement.h> +#include <linux/psb_drm.h> +#include <xf86drm.h> +#include <HwcTrace.h> + +struct _WsbmBufferPool * mainPool = NULL; + +struct PsbWsbmValidateNode +{ + struct _ValidateNode base; + struct psb_validate_arg arg; +}; + +static inline uint32_t align_to(uint32_t arg, uint32_t align) +{ + return ((arg + (align - 1)) & (~(align - 1))); +} + +static struct _ValidateNode * pvrAlloc(struct _WsbmVNodeFuncs * func, + int typeId) +{ + CTRACE(); + if(typeId == 0) { + struct PsbWsbmValidateNode * vNode = malloc(sizeof(*vNode)); + if(!vNode) { + ETRACE("failed to allocate memory"); + return NULL; + } + + vNode->base.func = func; + vNode->base.type_id = 0; + return &vNode->base; + } else { + struct _ValidateNode * node = malloc(sizeof(*node)); + if(!node) { + ETRACE("failed to allocate node"); + return NULL; + } + + node->func = func; + node->type_id = 1; + return node; + } +} + +static void pvrFree(struct _ValidateNode * node) +{ + CTRACE(); + if(node->type_id == 0) { + free(containerOf(node, struct PsbWsbmValidateNode, base)); + } else { + free(node); + } +} + +static void pvrClear(struct _ValidateNode * node) +{ + CTRACE(); + if(node->type_id == 0) { + struct PsbWsbmValidateNode * vNode = + containerOf(node, struct PsbWsbmValidateNode, base); + memset(&vNode->arg.d.req, 0, sizeof(vNode->arg.d.req)); + } +} + +static struct _WsbmVNodeFuncs vNodeFuncs = { + .alloc = pvrAlloc, + .free = pvrFree, + .clear = pvrClear, +}; + +void psbWsbmTakedown() +{ + CTRACE(); + + if (mainPool) { + wsbmPoolTakeDown(mainPool); + mainPool = NULL; + } + + if (wsbmIsInitialized()) { + wsbmTakedown(); + } +} + +int psbWsbmInitialize(int drmFD) +{ + union drm_psb_extension_arg arg; + const char drmExt[] = "psb_ttm_placement_alphadrop"; + int ret = 0; + + CTRACE(); + + if (drmFD <= 0) { + ETRACE("invalid drm fd %d", drmFD); + return drmFD; + } + + /*init wsbm*/ + ret = wsbmInit(wsbmNullThreadFuncs(), &vNodeFuncs); + if (ret) { + ETRACE("failed to initialize Wsbm, error code %d", ret); + return ret; + } + + VTRACE("DRM_PSB_EXTENSION %d", DRM_PSB_EXTENSION); + + /*get devOffset via drm IOCTL*/ + strncpy(arg.extension, drmExt, sizeof(drmExt)); + + ret = drmCommandWriteRead(drmFD, 6/*DRM_PSB_EXTENSION*/, &arg, sizeof(arg)); + if(ret || !arg.rep.exists) { + ETRACE("failed to get device offset, error code %d", ret); + goto out; + } + + VTRACE("ioctl offset %#x", arg.rep.driver_ioctl_offset); + + mainPool = wsbmTTMPoolInit(drmFD, arg.rep.driver_ioctl_offset); + if(!mainPool) { + ETRACE("failed to initialize TTM Pool"); + ret = -EINVAL; + goto out; + } + + VTRACE("Wsbm initialization succeeded. mainPool %p", mainPool); + + return 0; + +out: + psbWsbmTakedown(); + return ret; +} + +int psbWsbmAllocateFromUB(uint32_t size, uint32_t align, void ** buf, void *user_pt) +{ + struct _WsbmBufferObject * wsbmBuf = NULL; + int ret = 0; + int offset = 0; + + ATRACE("size %d", align_to(size, 4096)); + + if(!buf || !user_pt) { + ETRACE("invalid parameter"); + return -EINVAL; + } + + VTRACE("mainPool %p", mainPool); + + ret = wsbmGenBuffers(mainPool, 1, &wsbmBuf, align, + DRM_PSB_FLAG_MEM_MMU | WSBM_PL_FLAG_CACHED | + WSBM_PL_FLAG_NO_EVICT | WSBM_PL_FLAG_SHARED); + if(ret) { + ETRACE("wsbmGenBuffers failed with error code %d", ret); + return ret; + } + + ret = wsbmBODataUB(wsbmBuf, + align_to(size, 4096), NULL, NULL, 0, + user_pt); + + if(ret) { + ETRACE("wsbmBOData failed with error code %d", ret); + /*FIXME: should I unreference this buffer here?*/ + return ret; + } + + *buf = wsbmBuf; + + VTRACE("ttm UB buffer allocated. %p", *buf); + return 0; +} + +int psbWsbmAllocateTTMBuffer(uint32_t size, uint32_t align, void ** buf) +{ + struct _WsbmBufferObject * wsbmBuf = NULL; + int ret = 0; + int offset = 0; + + ATRACE("size %d", align_to(size, 4096)); + + if(!buf) { + ETRACE("invalid parameter"); + return -EINVAL; + } + + VTRACE("mainPool %p", mainPool); + + ret = wsbmGenBuffers(mainPool, 1, &wsbmBuf, align, + (WSBM_PL_FLAG_VRAM | WSBM_PL_FLAG_TT | + WSBM_PL_FLAG_SHARED | WSBM_PL_FLAG_NO_EVICT)); + if(ret) { + ETRACE("wsbmGenBuffers failed with error code %d", ret); + return ret; + } + + ret = wsbmBOData(wsbmBuf, align_to(size, 4096), NULL, NULL, 0); + if(ret) { + ETRACE("wsbmBOData failed with error code %d", ret); + /*FIXME: should I unreference this buffer here?*/ + return ret; + } + + /* wsbmBOReference(wsbmBuf); */ /* no need to add reference */ + + *buf = wsbmBuf; + + VTRACE("ttm buffer allocated. %p", *buf); + return 0; +} + +int psbWsbmWrapTTMBuffer(uint64_t handle, void **buf) +{ + int ret = 0; + struct _WsbmBufferObject *wsbmBuf; + + if (!buf) { + ETRACE("invalid parameter"); + return -EINVAL; + } + + ret = wsbmGenBuffers(mainPool, 1, &wsbmBuf, 0, + (WSBM_PL_FLAG_VRAM | WSBM_PL_FLAG_TT | + /*WSBM_PL_FLAG_NO_EVICT |*/ WSBM_PL_FLAG_SHARED)); + + if (ret) { + ETRACE("wsbmGenBuffers failed with error code %d", ret); + return ret; + } + + ret = wsbmBOSetReferenced(wsbmBuf, handle); + if (ret) { + ETRACE("wsbmBOSetReferenced failed with error code %d", ret); + return ret; + } + + *buf = (void *)wsbmBuf; + + VTRACE("wrap buffer %p for handle %#x", wsbmBuf, handle); + return 0; +} + +int psbWsbmWrapTTMBuffer2(uint64_t handle, void **buf) +{ + int ret = 0; + struct _WsbmBufferObject *wsbmBuf; + + if (!buf) { + ETRACE("invalid parameter"); + return -EINVAL; + } + + ret = wsbmGenBuffers(mainPool, 1, &wsbmBuf, 4096, + (WSBM_PL_FLAG_SHARED | DRM_PSB_FLAG_MEM_MMU | WSBM_PL_FLAG_UNCACHED)); + + if (ret) { + ETRACE("wsbmGenBuffers failed with error code %d", ret); + return ret; + } + + *buf = (void *)wsbmBuf; + + VTRACE("wrap buffer %p for handle %#x", wsbmBuf, handle); + return 0; +} + + +int psbWsbmCreateFromUB(void *buf, uint32_t size, void *vaddr) +{ + int ret = 0; + struct _WsbmBufferObject *wsbmBuf; + + if (!buf || !vaddr) { + ETRACE("invalid parameter"); + return -EINVAL; + } + + wsbmBuf = (struct _WsbmBufferObject *)buf; + ret = wsbmBODataUB(wsbmBuf, size, NULL, NULL, 0, vaddr); + if (ret) { + ETRACE("wsbmBODataUB failed with error code %d", ret); + return ret; + } + + return 0; +} + +int psbWsbmUnReference(void *buf) +{ + struct _WsbmBufferObject *wsbmBuf; + + if (!buf) { + ETRACE("invalid parameter"); + return -EINVAL; + } + + wsbmBuf = (struct _WsbmBufferObject *)buf; + + wsbmBOUnreference(&wsbmBuf); + + return 0; +} + +int psbWsbmDestroyTTMBuffer(void * buf) +{ + CTRACE(); + + if(!buf) { + ETRACE("invalid ttm buffer"); + return -EINVAL; + } + + /*FIXME: should I unmap this buffer object first?*/ + wsbmBOUnmap((struct _WsbmBufferObject *)buf); + + wsbmBOUnreference((struct _WsbmBufferObject **)&buf); + + XTRACE(); + + return 0; +} + +void * psbWsbmGetCPUAddress(void * buf) +{ + if(!buf) { + ETRACE("invalid ttm buffer"); + return NULL; + } + + VTRACE("buffer object %p", buf); + + void * address = wsbmBOMap((struct _WsbmBufferObject *)buf, + WSBM_ACCESS_READ | WSBM_ACCESS_WRITE); + if(!address) { + ETRACE("failed to map buffer object"); + return NULL; + } + + VTRACE("mapped successfully. %p, size %ld", + address, wsbmBOSize((struct _WsbmBufferObject *)buf)); + + return address; +} + +uint32_t psbWsbmGetGttOffset(void * buf) +{ + if(!buf) { + ETRACE("invalid ttm buffer"); + return 0; + } + + VTRACE("buffer object %p", buf); + + uint32_t offset = + wsbmBOOffsetHint((struct _WsbmBufferObject *)buf) - 0x10000000; + + VTRACE("offset %#x", offset >> 12); + + return offset >> 12; +} + +uint32_t psbWsbmGetKBufHandle(void *buf) +{ + if (!buf) { + ETRACE("invalid ttm buffer"); + return 0; + } + + return (wsbmKBufHandle(wsbmKBuf((struct _WsbmBufferObject *)buf))); +} + +uint32_t psbWsbmWaitIdle(void *buf) +{ + if (!buf) { + ETRACE("invalid ttm buffer"); + return -EINVAL; + } + + wsbmBOWaitIdle(buf, 0); + return 0; +} diff --git a/merrifield/ips/common/WsbmWrapper.h b/merrifield/ips/common/WsbmWrapper.h new file mode 100644 index 0000000..1dad5fd --- /dev/null +++ b/merrifield/ips/common/WsbmWrapper.h @@ -0,0 +1,41 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef WSBM_WRAPPER_H +#define WSBM_WRAPPER_H + +#if defined(__cplusplus) +extern "C" { +#endif + +extern int psbWsbmInitialize(int drmFD); +extern void psbWsbmTakedown(); +extern int psbWsbmAllocateFromUB(uint32_t size, uint32_t align, void ** buf, void *user_pt); +extern int psbWsbmAllocateTTMBuffer(uint32_t size, uint32_t align,void ** buf); +extern int psbWsbmDestroyTTMBuffer(void * buf); +extern void * psbWsbmGetCPUAddress(void * buf); +extern uint32_t psbWsbmGetGttOffset(void * buf); +extern int psbWsbmWrapTTMBuffer(uint64_t handle, void **buf); +extern int psbWsbmWrapTTMBuffer2(uint64_t handle, void **buf); +extern int psbWsbmCreateFromUB(void *buf, uint32_t size, void *vaddr); +extern int psbWsbmUnReference(void *buf); +extern int psbWsbmWaitIdle(void *buf); +uint32_t psbWsbmGetKBufHandle(void *buf); + +#if defined(__cplusplus) +} +#endif + +#endif /*WSBM_WRAPPER_H*/ diff --git a/merrifield/ips/penwell/PnwGrallocBuffer.cpp b/merrifield/ips/penwell/PnwGrallocBuffer.cpp new file mode 100644 index 0000000..a867cb8 --- /dev/null +++ b/merrifield/ips/penwell/PnwGrallocBuffer.cpp @@ -0,0 +1,46 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <HwcTrace.h> +#include <penwell/PnwGrallocBuffer.h> + +namespace android { +namespace intel { + +PnwGrallocBuffer::PnwGrallocBuffer(uint32_t handle) + :GrallocBufferBase(handle) +{ + struct PnwIMGGrallocBuffer *grallocHandle = + (struct PnwIMGGrallocBuffer*)handle; + + CTRACE(); + + if (!grallocHandle) { + ETRACE("gralloc handle is null"); + return; + } + + mFormat = grallocHandle->format; + mWidth = grallocHandle->width; + mHeight = grallocHandle->height; + mUsage = grallocHandle->usage; + mKey = grallocHandle->stamp; + mBpp = grallocHandle->bpp; + + initialize(); +} + +} +} diff --git a/merrifield/ips/penwell/PnwGrallocBuffer.h b/merrifield/ips/penwell/PnwGrallocBuffer.h new file mode 100644 index 0000000..ce0414c --- /dev/null +++ b/merrifield/ips/penwell/PnwGrallocBuffer.h @@ -0,0 +1,46 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef PNW_GRALLOC_BUFFER_H +#define PNW_GRALLOC_BUFFER_H + +#include <common/GrallocSubBuffer.h> +#include <common/GrallocBufferBase.h> + +namespace android { +namespace intel { + +struct PnwIMGGrallocBuffer{ + native_handle_t base; + int fd[SUB_BUFFER_MAX]; + unsigned long long stamp; + int usage; + int width; + int height; + int format; + int bpp; +}__attribute__((aligned(sizeof(int)),packed)); + + +class PnwGrallocBuffer : public GrallocBufferBase { +public: + PnwGrallocBuffer(uint32_t handle); +}; + +} // namespace intel +} // namespace android + + +#endif /* PNW_GRALLOC_BUFFER_H */ diff --git a/merrifield/ips/penwell/PnwGrallocBufferMapper.cpp b/merrifield/ips/penwell/PnwGrallocBufferMapper.cpp new file mode 100644 index 0000000..2550437 --- /dev/null +++ b/merrifield/ips/penwell/PnwGrallocBufferMapper.cpp @@ -0,0 +1,48 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <HwcTrace.h> +#include <Drm.h> +#include <Hwcomposer.h> +#include <penwell/PnwGrallocBufferMapper.h> + +namespace android { +namespace intel { + +PnwGrallocBufferMapper::PnwGrallocBufferMapper(DataBuffer& buffer) + : GrallocBufferMapperBase(buffer) +{ + CTRACE(); +} + +PnwGrallocBufferMapper::~PnwGrallocBufferMapper() +{ + CTRACE(); +} + +bool PnwGrallocBufferMapper::map() +{ + // TODO: implement map + return false; +} + +bool PnwGrallocBufferMapper::unmap() +{ + //TODO: implement unmap + return false; +} + +} // namespace intel +} // namespace android diff --git a/merrifield/ips/penwell/PnwGrallocBufferMapper.h b/merrifield/ips/penwell/PnwGrallocBufferMapper.h new file mode 100644 index 0000000..743d498 --- /dev/null +++ b/merrifield/ips/penwell/PnwGrallocBufferMapper.h @@ -0,0 +1,38 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef PNW_GRALLOC_BUFFER_MAPPER_H +#define PNW_GRALLOC_BUFFER_MAPPER_H + +#include <BufferMapper.h> +#include <hal_public.h> +#include <common/GrallocBufferMapperBase.h> + +namespace android { +namespace intel { + +class PnwGrallocBufferMapper : public GrallocBufferMapperBase { +public: + PnwGrallocBufferMapper(DataBuffer& buffer); + ~PnwGrallocBufferMapper(); +public: + bool map(); + bool unmap(); +}; + +} // namespace intel +} // namespace android + +#endif /* TNG_GRALLOC_BUFFER_MAPPER_H */ diff --git a/merrifield/ips/penwell/PnwOverlayPlane.cpp b/merrifield/ips/penwell/PnwOverlayPlane.cpp new file mode 100644 index 0000000..f2328fa --- /dev/null +++ b/merrifield/ips/penwell/PnwOverlayPlane.cpp @@ -0,0 +1,51 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <math.h> +#include <HwcTrace.h> +#include <Drm.h> +#include <Hwcomposer.h> +#include <penwell/PnwOverlayPlane.h> +#include <penwell/PnwGrallocBuffer.h> + +namespace android { +namespace intel { + +PnwOverlayPlane::PnwOverlayPlane(int index, int disp) + : OverlayPlaneBase(index, disp) +{ + CTRACE(); +} + +PnwOverlayPlane::~PnwOverlayPlane() +{ + CTRACE(); +} + +bool PnwOverlayPlane::flip() +{ + //TODO: implement flip + return false; +} + +void* PnwOverlayPlane::getContext() const +{ + CTRACE(); + //TODO: return penwell overlay context + return 0; +} + +} // namespace intel +} // namespace android diff --git a/merrifield/ips/penwell/PnwOverlayPlane.h b/merrifield/ips/penwell/PnwOverlayPlane.h new file mode 100644 index 0000000..0ae3924 --- /dev/null +++ b/merrifield/ips/penwell/PnwOverlayPlane.h @@ -0,0 +1,42 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef PNW_OVERLAY_PLANE_H +#define PNW_OVERLAY_PLANE_H + +#include <utils/KeyedVector.h> +#include <hal_public.h> +#include <DisplayPlane.h> +#include <BufferMapper.h> +#include <common/Wsbm.h> +#include <common/OverlayPlaneBase.h> + +namespace android { +namespace intel { + +class PnwOverlayPlane : public OverlayPlaneBase { + +public: + PnwOverlayPlane(int index, int disp); + ~PnwOverlayPlane(); + + virtual bool flip(); + virtual void* getContext() const; +}; + +} // namespace intel +} // namespace android + +#endif /* PNW_OVERLAY_PLANE_H */ diff --git a/merrifield/ips/penwell/PnwPrimaryPlane.cpp b/merrifield/ips/penwell/PnwPrimaryPlane.cpp new file mode 100644 index 0000000..c88d116 --- /dev/null +++ b/merrifield/ips/penwell/PnwPrimaryPlane.cpp @@ -0,0 +1,65 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <HwcTrace.h> +#include <Drm.h> +#include <penwell/PnwPrimaryPlane.h> +#include <penwell/PnwGrallocBuffer.h> +#include <common/PixelFormat.h> + +namespace android { +namespace intel { + +PnwPrimaryPlane::PnwPrimaryPlane(int index, int disp) + : PnwSpritePlane(index, disp) +{ + CTRACE(); + mType = PLANE_PRIMARY; +} + +PnwPrimaryPlane::~PnwPrimaryPlane() +{ + CTRACE(); +} + +void PnwPrimaryPlane::setFramebufferTarget(DataBuffer& buf) +{ + CTRACE(); + //TODO: implement penwell frame buffer target flip +} + +bool PnwPrimaryPlane::setDataBuffer(uint32_t handle) +{ + PnwGrallocBuffer tmpBuf(handle); + uint32_t usage; + + ATRACE("handle = %#x", handle); + + usage = tmpBuf.getUsage(); + if (!handle || (GRALLOC_USAGE_HW_FB & usage)) { + setFramebufferTarget(tmpBuf); + return true; + } + + return DisplayPlane::setDataBuffer(handle); +} + +bool PnwPrimaryPlane::assignToDevice(int disp) +{ + return true; +} + +} // namespace intel +} // namespace android diff --git a/merrifield/ips/penwell/PnwPrimaryPlane.h b/merrifield/ips/penwell/PnwPrimaryPlane.h new file mode 100644 index 0000000..7f8b443 --- /dev/null +++ b/merrifield/ips/penwell/PnwPrimaryPlane.h @@ -0,0 +1,38 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef PNW_PRIMARY_PLANE_H +#define PNW_PRIMARY_PLANE_H + +#include <penwell/PnwSpritePlane.h> + +namespace android { +namespace intel { + +class PnwPrimaryPlane : public PnwSpritePlane { +public: + PnwPrimaryPlane(int index, int disp); + ~PnwPrimaryPlane(); +public: + bool setDataBuffer(uint32_t handle); + bool assignToDevice(int disp); +private: + void setFramebufferTarget(DataBuffer& buf); +}; + +} // namespace intel +} // namespace android + +#endif /* TNG_PRIMARY_PLANE_H */ diff --git a/merrifield/ips/penwell/PnwSpritePlane.cpp b/merrifield/ips/penwell/PnwSpritePlane.cpp new file mode 100644 index 0000000..6799b1a --- /dev/null +++ b/merrifield/ips/penwell/PnwSpritePlane.cpp @@ -0,0 +1,50 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <HwcTrace.h> +#include <Hwcomposer.h> +#include <BufferManager.h> +#include <penwell/PnwSpritePlane.h> +#include <common/PixelFormat.h> + +namespace android { +namespace intel { + +PnwSpritePlane::PnwSpritePlane(int index, int disp) + : SpritePlaneBase(index, disp) +{ + CTRACE(); +} + +PnwSpritePlane::~PnwSpritePlane() +{ + CTRACE(); +} + +bool PnwSpritePlane::setDataBuffer(BufferMapper& mapper) +{ + // TODO: implement setDataBuffer + return false; +} + +void* PnwSpritePlane::getContext() const +{ + CTRACE(); + // TODO: return penwell sprite context + return 0; +} + +} // namespace intel +} // namespace android diff --git a/merrifield/ips/penwell/PnwSpritePlane.h b/merrifield/ips/penwell/PnwSpritePlane.h new file mode 100644 index 0000000..189ef1b --- /dev/null +++ b/merrifield/ips/penwell/PnwSpritePlane.h @@ -0,0 +1,42 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef PNW_SPRITE_PLANE_H +#define PNW_SPRITE_PLANE_H + +#include <utils/KeyedVector.h> +#include <hal_public.h> +#include <BufferCache.h> +#include <DisplayPlane.h> + +#include <common/SpritePlaneBase.h> + +namespace android { +namespace intel { + +class PnwSpritePlane : public SpritePlaneBase { +public: + PnwSpritePlane(int index, int disp); + virtual ~PnwSpritePlane(); +public: + virtual void* getContext() const; +protected: + virtual bool setDataBuffer(BufferMapper& mapper); +}; + +} // namespace intel +} // namespace android + +#endif /* PNW_SPRITE_PLANE_H */ diff --git a/merrifield/ips/tangier/TngCursorPlane.cpp b/merrifield/ips/tangier/TngCursorPlane.cpp new file mode 100644 index 0000000..8ce5af7 --- /dev/null +++ b/merrifield/ips/tangier/TngCursorPlane.cpp @@ -0,0 +1,244 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <HwcTrace.h> +#include <Hwcomposer.h> +#include <BufferManager.h> +#include <BufferManager.h> +#include <tangier/TngCursorPlane.h> +#include <tangier/TngGrallocBuffer.h> +#include <hal_public.h> + +namespace android { +namespace intel { + +TngCursorPlane::TngCursorPlane(int index, int disp) + : DisplayPlane(index, PLANE_CURSOR, disp) +{ + CTRACE(); + memset(&mContext, 0, sizeof(mContext)); + memset(&mCrop, 0, sizeof(mCrop)); +} + +TngCursorPlane::~TngCursorPlane() +{ + CTRACE(); +} + +bool TngCursorPlane::enable() +{ + return enablePlane(true); +} + +bool TngCursorPlane::disable() +{ + return enablePlane(false); +} + +void* TngCursorPlane::getContext() const +{ + CTRACE(); + return (void *)&mContext; +} + +void TngCursorPlane::setZOrderConfig(ZOrderConfig& config, void *nativeConfig) +{ + (void) config; + (void) nativeConfig; + + VTRACE("\n *** need to implement zorder config *** "); + CTRACE(); +} + +bool TngCursorPlane::setDataBuffer(buffer_handle_t handle) +{ + bool ret; + + if (!handle) { + ETRACE("handle is NULL"); + return false; + } + + ret = DisplayPlane::setDataBuffer(handle); + if (ret == false) { + ETRACE("failed to set data buffer"); + return ret; + } + + return true; +} + +bool TngCursorPlane::setDataBuffer(BufferMapper& mapper) +{ + int w = mapper.getWidth(); + int h = mapper.getHeight(); + int cursorSize = 0; + + CTRACE(); + + // setup plane position + int dstX = mPosition.x; + int dstY = mPosition.y; + + if (h < w) { + cursorSize = h; + } else { + cursorSize = w; + } + + uint32_t cntr = 0; + if (64 <= cursorSize && cursorSize < 128) { + cursorSize = 64; + cntr = 0x7; + } else if (128 <= cursorSize && cursorSize < 256) { + cursorSize = 128; + cntr = 0x2; + } else { + cursorSize = 256; + cntr = 0x3; + } + + if (mapper.getFormat() == HAL_PIXEL_FORMAT_RGBA_8888) { + cntr |= 1 << 5; + } else if (mapper.getFormat() == HAL_PIXEL_FORMAT_BGRA_8888) { + // swap color from BGRA to RGBA - alpha is MSB + uint8_t *p = (uint8_t *)(mapper.getCpuAddress(0)); + uint8_t *srcPixel; + uint32_t stride = mapper.getStride().rgb.stride; + uint8_t temp; + if (!p) { + return false; + } + + for (int i = 0; i < cursorSize; i++) { + for (int j = 0; j < cursorSize; j++) { + srcPixel = p + i*stride + j*4; + temp = srcPixel[0]; + srcPixel[0] = srcPixel[2]; + srcPixel[2] = temp; + } + } + cntr |= 1 << 5; + } else { + ETRACE("invalid color format"); + return false; + } + + // TODO: clean spare mem to be 0 in gralloc instead + uint8_t *p = (uint8_t *)(mapper.getCpuAddress(0)); + uint8_t *srcPixel; + uint32_t stride = mapper.getStride().rgb.stride; + uint8_t temp; + if (!p) { + return false; + } + + if (mCrop.w == 0 && mCrop.h == 0) { + mCrop = mSrcCrop; + for (int i = 0; i < cursorSize; i++) { + for (int j = 0; j < cursorSize; j++) { + srcPixel = p + i*stride + j*4; + temp = srcPixel[0]; + if (i >= mCrop.h || j >= mCrop.w) { + if (srcPixel[0] == 0 && + srcPixel[3] == 0xff) + srcPixel[3] = 0; + } + } + } + } + + // update context + mContext.type = DC_CURSOR_PLANE; + mContext.ctx.cs_ctx.index = mIndex; + mContext.ctx.cs_ctx.pipe = mDevice; + mContext.ctx.cs_ctx.cntr = cntr | (mIndex << 28); + mContext.ctx.cs_ctx.surf = mapper.getGttOffsetInPage(0) << 12; + + mContext.ctx.cs_ctx.pos = 0; + if (dstX < 0) { + mContext.ctx.cs_ctx.pos |= 1 << 15; + dstX = -dstX; + } + if (dstY < 0) { + mContext.ctx.cs_ctx.pos |= 1 << 31; + dstY = -dstY; + } + mContext.ctx.cs_ctx.pos |= (dstY & 0xfff) << 16 | (dstX & 0xfff); + return true; +} + +bool TngCursorPlane::enablePlane(bool enabled) +{ + RETURN_FALSE_IF_NOT_INIT(); + + struct drm_psb_register_rw_arg arg; + memset(&arg, 0, sizeof(struct drm_psb_register_rw_arg)); + if (enabled) { + arg.plane_enable_mask = 1; + } else { + arg.plane_disable_mask = 1; + } + + arg.plane.type = DC_CURSOR_PLANE; + arg.plane.index = mIndex; + arg.plane.ctx = 0; + + // issue ioctl + Drm *drm = Hwcomposer::getInstance().getDrm(); + bool ret = drm->writeReadIoctl(DRM_PSB_REGISTER_RW, &arg, sizeof(arg)); + if (ret == false) { + WTRACE("plane enabling (%d) failed with error code %d", enabled, ret); + return false; + } + + return true; +} + +bool TngCursorPlane::isDisabled() +{ + RETURN_FALSE_IF_NOT_INIT(); + + struct drm_psb_register_rw_arg arg; + memset(&arg, 0, sizeof(struct drm_psb_register_rw_arg)); + + arg.plane.type = DC_CURSOR_PLANE; + arg.get_plane_state_mask = 1; + arg.plane.index = mIndex; + arg.plane.ctx = 0; + + // issue ioctl + Drm *drm = Hwcomposer::getInstance().getDrm(); + bool ret = drm->writeReadIoctl(DRM_PSB_REGISTER_RW, &arg, sizeof(arg)); + if (ret == false) { + WTRACE("plane state query failed with error code %d", ret); + return false; + } + + return arg.plane.ctx == PSB_DC_PLANE_DISABLED; + //return arg.plane.ctx == 0; //implement this PSB_DC_PLANE_DISABLED similar in imin_legacy + + return true; +} + +void TngCursorPlane::postFlip() +{ + // prevent mUpdateMasks from being reset + // skipping flip may cause flicking +} + +} // namespace intel +} // namespace android diff --git a/merrifield/ips/tangier/TngCursorPlane.h b/merrifield/ips/tangier/TngCursorPlane.h new file mode 100644 index 0000000..85e869a --- /dev/null +++ b/merrifield/ips/tangier/TngCursorPlane.h @@ -0,0 +1,58 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef TNG_CUR_PLANE_H +#define TNG_CUR_PLANE_H + +#include <utils/KeyedVector.h> +#include <hal_public.h> +#include <Hwcomposer.h> +#include <BufferCache.h> +#include <DisplayPlane.h> + +#include <linux/psb_drm.h> + +namespace android { +namespace intel { + +class TngCursorPlane : public DisplayPlane { +public: + TngCursorPlane(int index, int disp); + virtual ~TngCursorPlane(); +public: + // hardware operations + bool enable(); + bool disable(); + bool isDisabled(); + void postFlip(); + + void* getContext() const; + void setZOrderConfig(ZOrderConfig& config, void *nativeConfig); + + bool setDataBuffer(buffer_handle_t handle); +protected: + bool setDataBuffer(BufferMapper& mapper); + bool enablePlane(bool enabled); + +protected: + struct intel_dc_plane_ctx mContext; + crop_t mCrop; +}; + +} // namespace intel +} // namespace android + +#endif /* TNG_CUR_PLANE_H */ diff --git a/merrifield/ips/tangier/TngDisplayContext.cpp b/merrifield/ips/tangier/TngDisplayContext.cpp new file mode 100644 index 0000000..7f94185 --- /dev/null +++ b/merrifield/ips/tangier/TngDisplayContext.cpp @@ -0,0 +1,278 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <HwcTrace.h> +#include <Hwcomposer.h> +#include <Drm.h> +#include <DisplayPlane.h> +#include <IDisplayDevice.h> +#include <HwcLayerList.h> +#include <tangier/TngDisplayContext.h> + + +namespace android { +namespace intel { + +TngDisplayContext::TngDisplayContext() + : mIMGDisplayDevice(0), + mInitialized(false), + mCount(0) +{ + CTRACE(); +} + +TngDisplayContext::~TngDisplayContext() +{ + WARN_IF_NOT_DEINIT(); +} + +bool TngDisplayContext::initialize() +{ + CTRACE(); + + // open frame buffer device + hw_module_t const* module; + int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module); + if (err) { + ETRACE("failed to load gralloc module, error = %d", err); + return false; + } + + // init IMG display device + mIMGDisplayDevice = (((IMG_gralloc_module_public_t *)module)->getDisplayDevice((IMG_gralloc_module_public_t *)module)); + if (!mIMGDisplayDevice) { + ETRACE("failed to get display device"); + return false; + } + + mCount = 0; + mInitialized = true; + return true; +} + +bool TngDisplayContext::commitBegin(size_t numDisplays, hwc_display_contents_1_t **displays) +{ + RETURN_FALSE_IF_NOT_INIT(); + mCount = 0; + return true; +} + +bool TngDisplayContext::commitContents(hwc_display_contents_1_t *display, HwcLayerList *layerList) +{ + bool ret; + + RETURN_FALSE_IF_NOT_INIT(); + + if (!display || !layerList) { + ETRACE("invalid parameters"); + return false; + } + + IMG_hwc_layer_t *imgLayerList = (IMG_hwc_layer_t*)mImgLayers; + + for (size_t i = 0; i < display->numHwLayers; i++) { + if (mCount >= MAXIMUM_LAYER_NUMBER) { + ETRACE("layer count exceeds the limit"); + return false; + } + + // check layer parameters + if (!display->hwLayers[i].handle) { + continue; + } + + DisplayPlane* plane = layerList->getPlane(i); + if (!plane) { + continue; + } + + ret = plane->flip(NULL); + if (ret == false) { + VTRACE("failed to flip plane %d", i); + continue; + } + + IMG_hwc_layer_t *imgLayer = &imgLayerList[mCount++]; + // update IMG layer + imgLayer->psLayer = &display->hwLayers[i]; + imgLayer->custom = (unsigned long)plane->getContext(); + struct intel_dc_plane_ctx *ctx = + (struct intel_dc_plane_ctx *)imgLayer->custom; + // update z order + Hwcomposer& hwc = Hwcomposer::getInstance(); + DisplayPlaneManager *pm = hwc.getPlaneManager(); + void *config = pm->getZOrderConfig(); + if (config) { + memcpy(&ctx->zorder, config, sizeof(ctx->zorder)); + } else { + memset(&ctx->zorder, 0, sizeof(ctx->zorder)); + } + + VTRACE("count %p, handle %#x, trans %#x, blending %#x" + " sourceCrop %f,%f - %fx%f, dst %d,%d - %dx%d, custom %#x", + mCount, + imgLayer->psLayer->handle, + imgLayer->psLayer->transform, + imgLayer->psLayer->blending, + imgLayer->psLayer->sourceCropf.left, + imgLayer->psLayer->sourceCropf.top, + imgLayer->psLayer->sourceCropf.right - imgLayer->psLayer->sourceCropf.left, + imgLayer->psLayer->sourceCropf.bottom - imgLayer->psLayer->sourceCropf.top, + imgLayer->psLayer->displayFrame.left, + imgLayer->psLayer->displayFrame.top, + imgLayer->psLayer->displayFrame.right - imgLayer->psLayer->displayFrame.left, + imgLayer->psLayer->displayFrame.bottom - imgLayer->psLayer->displayFrame.top, + imgLayer->custom); + } + + layerList->postFlip(); + return true; +} + +bool TngDisplayContext::commitEnd(size_t numDisplays, hwc_display_contents_1_t **displays) +{ + int releaseFenceFd = -1; + + VTRACE("count = %d", mCount); + + if (mIMGDisplayDevice && mCount) { + int err = mIMGDisplayDevice->post(mIMGDisplayDevice, + mImgLayers, + mCount, + &releaseFenceFd); + if (err) { + ETRACE("post failed, err = %d", err); + return false; + } + } + + // close acquire fence + for (size_t i = 0; i < numDisplays; i++) { + // Wait and close HWC_OVERLAY typed layer's acquire fence + hwc_display_contents_1_t* display = displays[i]; + if (!display) { + continue; + } + + for (size_t j = 0; j < display->numHwLayers-1; j++) { + hwc_layer_1_t& layer = display->hwLayers[j]; + if (layer.compositionType == HWC_OVERLAY) { + if (layer.acquireFenceFd != -1) { + // sync_wait(layer.acquireFenceFd, 16ms); + close(layer.acquireFenceFd); + layer.acquireFenceFd = -1; + } + } + } + + // Wait and close framebuffer target layer's acquire fence + hwc_layer_1_t& fbt = display->hwLayers[display->numHwLayers-1]; + if (fbt.acquireFenceFd != -1) { + // sync_wait(fbt.acquireFencdFd, 16ms); + close(fbt.acquireFenceFd); + fbt.acquireFenceFd = -1; + } + + // Wait and close outbuf's acquire fence + if (display->outbufAcquireFenceFd != -1) { + // sync_wait(display->outbufAcquireFenceFd, 16ms); + close(display->outbufAcquireFenceFd); + display->outbufAcquireFenceFd = -1; + } + } + + // update release fence and retire fence + if (mCount > 0) { + // For physical displays, dup the releaseFenceFd only for + // HWC layers which successfully flipped to display planes + IMG_hwc_layer_t *imgLayerList = (IMG_hwc_layer_t*)mImgLayers; + + for (unsigned int i = 0; i < mCount; i++) { + IMG_hwc_layer_t *imgLayer = &imgLayerList[i]; + imgLayer->psLayer->releaseFenceFd = + (releaseFenceFd != -1) ? dup(releaseFenceFd) : -1; + } + } + + for (size_t i = 0; i < numDisplays; i++) { + if (!displays[i]) { + continue; + } + + // log for layer fence status + for (size_t j = 0; j < displays[i]->numHwLayers; j++) { + VTRACE("handle %#p, acquiredFD %d, releaseFD %d", + displays[i]->hwLayers[j].handle, + displays[i]->hwLayers[j].acquireFenceFd, + displays[i]->hwLayers[j].releaseFenceFd); + } + +#ifdef INTEL_WIDI_MERRIFIELD + // retireFence is used for SurfaceFlinger to do DispSync; + // dup releaseFenceFd for physical displays and ignore virtual + // display; we don't distinguish between release and retire, and all + // physical displays are using a single releaseFence; for virtual + // display, fencing is handled by the VirtualDisplay class + if (i < IDisplayDevice::DEVICE_VIRTUAL) { +#endif + displays[i]->retireFenceFd = + (releaseFenceFd != -1) ? dup(releaseFenceFd) : -1; +#ifdef INTEL_WIDI_MERRIFIELD + } +#endif + } + + // close original release fence fd + if (releaseFenceFd != -1) { + close(releaseFenceFd); + } + return true; +} + +bool TngDisplayContext::compositionComplete() +{ + return true; +} + +bool TngDisplayContext::setCursorPosition(int disp, int x, int y) +{ + DTRACE("setCursorPosition"); + struct intel_dc_cursor_ctx ctx; + memset(&ctx, 0, sizeof(ctx)); + ctx.pipe = disp; + if (x < 0) { + ctx.pos |= 1 << 15; + x = -x; + } + if (y < 0) { + ctx.pos |= 1 << 31; + y = -y; + } + ctx.pos |= (y & 0xfff) << 16 | (x & 0xfff); + Drm *drm = Hwcomposer::getInstance().getDrm(); + return drm->writeIoctl(DRM_PSB_UPDATE_CURSOR_POS, &ctx, sizeof(ctx)); +} + +void TngDisplayContext::deinitialize() +{ + mIMGDisplayDevice = 0; + + mCount = 0; + mInitialized = false; +} + + +} // namespace intel +} // namespace android diff --git a/merrifield/ips/tangier/TngDisplayContext.h b/merrifield/ips/tangier/TngDisplayContext.h new file mode 100644 index 0000000..fa526d7 --- /dev/null +++ b/merrifield/ips/tangier/TngDisplayContext.h @@ -0,0 +1,51 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef TNG_DISPLAY_CONTEXT_H +#define TNG_DISPLAY_CONTEXT_H + +#include <IDisplayContext.h> +#include <hal_public.h> + +namespace android { +namespace intel { + +class TngDisplayContext : public IDisplayContext { +public: + TngDisplayContext(); + virtual ~TngDisplayContext(); +public: + bool initialize(); + void deinitialize(); + bool commitBegin(size_t numDisplays, hwc_display_contents_1_t **displays); + bool commitContents(hwc_display_contents_1_t *display, HwcLayerList* layerList); + bool commitEnd(size_t numDisplays, hwc_display_contents_1_t **displays); + bool compositionComplete(); + bool setCursorPosition(int disp, int x, int y); + +private: + enum { + MAXIMUM_LAYER_NUMBER = 20, + }; + IMG_display_device_public_t *mIMGDisplayDevice; + IMG_hwc_layer_t mImgLayers[MAXIMUM_LAYER_NUMBER]; + bool mInitialized; + size_t mCount; +}; + +} // namespace intel +} // namespace android + +#endif /* TNG_DISPLAY_CONTEXT_H */ diff --git a/merrifield/ips/tangier/TngDisplayQuery.cpp b/merrifield/ips/tangier/TngDisplayQuery.cpp new file mode 100644 index 0000000..c25aecf --- /dev/null +++ b/merrifield/ips/tangier/TngDisplayQuery.cpp @@ -0,0 +1,63 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <HwcTrace.h> +#include <DisplayPlane.h> +#include <hal_public.h> +#include <OMX_IVCommon.h> +#include <OMX_IntelVideoExt.h> +#include <DisplayQuery.h> + + +namespace android { +namespace intel { + +bool DisplayQuery::isVideoFormat(uint32_t format) +{ + switch (format) { + case OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar: + case OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar_Tiled: + // Expand format to support the case: Software decoder + HW rendering + // Only VP9 use this foramt now + case HAL_PIXEL_FORMAT_YV12: + return true; + default: + return false; + } +} + +int DisplayQuery::getOverlayLumaStrideAlignment(uint32_t format) +{ + // both luma and chroma stride need to be 64-byte aligned for overlay + switch (format) { + case HAL_PIXEL_FORMAT_YV12: + case HAL_PIXEL_FORMAT_I420: + // for these two formats, chroma stride is calculated as half of luma stride + // so luma stride needs to be 128-byte aligned. + return 128; + default: + return 64; + } +} + +uint32_t DisplayQuery::queryNV12Format() +{ + return HAL_PIXEL_FORMAT_NV12; +} + +} // namespace intel +} // namespace android + diff --git a/merrifield/ips/tangier/TngGrallocBuffer.cpp b/merrifield/ips/tangier/TngGrallocBuffer.cpp new file mode 100644 index 0000000..b66b896 --- /dev/null +++ b/merrifield/ips/tangier/TngGrallocBuffer.cpp @@ -0,0 +1,62 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <HwcTrace.h> +#include <tangier/TngGrallocBuffer.h> + +namespace android { +namespace intel { + +TngGrallocBuffer::TngGrallocBuffer(buffer_handle_t handle) + :GrallocBufferBase(handle) +{ + initBuffer(handle); +} + +TngGrallocBuffer::~TngGrallocBuffer() +{ +} + +void TngGrallocBuffer::resetBuffer(buffer_handle_t handle) +{ + GrallocBufferBase::resetBuffer(handle); + initBuffer(handle); +} + +void TngGrallocBuffer::initBuffer(buffer_handle_t handle) +{ + TngIMGGrallocBuffer *grallocHandle = (TngIMGGrallocBuffer *)handle; + + CTRACE(); + + if (!grallocHandle) { + ETRACE("gralloc handle is null"); + return; + } + + mFormat = grallocHandle->iFormat; + mWidth = grallocHandle->iWidth; + mHeight = grallocHandle->iHeight; + mUsage = grallocHandle->usage; + mKey = grallocHandle->ui64Stamp; + mBpp = grallocHandle->uiBpp; + + // stride can only be initialized after format is set + initStride(); +} + + +} +} diff --git a/merrifield/ips/tangier/TngGrallocBuffer.h b/merrifield/ips/tangier/TngGrallocBuffer.h new file mode 100644 index 0000000..b528cf8 --- /dev/null +++ b/merrifield/ips/tangier/TngGrallocBuffer.h @@ -0,0 +1,44 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef TNG_GRALLOC_BUFFER_H +#define TNG_GRALLOC_BUFFER_H + +#include <DataBuffer.h> +#include <hal_public.h> +#include <common/GrallocSubBuffer.h> +#include <common/GrallocBufferBase.h> + +namespace android { +namespace intel { + +typedef IMG_native_handle_t TngIMGGrallocBuffer; + +class TngGrallocBuffer : public GrallocBufferBase { +public: + TngGrallocBuffer(buffer_handle_t handle); + virtual ~TngGrallocBuffer(); + + void resetBuffer(buffer_handle_t handle); + +private: + void initBuffer(buffer_handle_t handle); +}; + +} // namespace intel +} // namespace android + + +#endif /* TNG_GRALLOC_BUFFER_H */ diff --git a/merrifield/ips/tangier/TngGrallocBufferMapper.cpp b/merrifield/ips/tangier/TngGrallocBufferMapper.cpp new file mode 100644 index 0000000..fcf40e7 --- /dev/null +++ b/merrifield/ips/tangier/TngGrallocBufferMapper.cpp @@ -0,0 +1,263 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <HwcTrace.h> +#include <Drm.h> +#include <Hwcomposer.h> +#include <tangier/TngGrallocBufferMapper.h> +#include <common/WsbmWrapper.h> + +namespace android { +namespace intel { + +TngGrallocBufferMapper::TngGrallocBufferMapper(IMG_gralloc_module_public_t& module, + DataBuffer& buffer) + : GrallocBufferMapperBase(buffer), + mIMGGrallocModule(module), + mBufferObject(0) +{ + CTRACE(); + + const native_handle_t *h = (native_handle_t *)mHandle; + + mClonedHandle = native_handle_create(h->numFds, h->numInts); + if (mClonedHandle == 0) { + ALOGE("%s:Failed to create handle, out of memory!"); + return; + } + for (int i = 0; i < h->numFds; i++) + { + mClonedHandle->data[i] = (h->data[i] >= 0) ? dup(h->data[i]) : -1; + } + memcpy(mClonedHandle->data + h->numFds, h->data + h->numFds, h->numInts*sizeof(int)); +} + +TngGrallocBufferMapper::~TngGrallocBufferMapper() +{ + CTRACE(); + + if (mClonedHandle == 0) + return; + native_handle_close(mClonedHandle); + native_handle_delete(mClonedHandle); +} + +bool TngGrallocBufferMapper::gttMap(void *vaddr, + uint32_t size, + uint32_t gttAlign, + int *offset) +{ + struct psb_gtt_mapping_arg arg; + bool ret; + + ATRACE("vaddr = %p, size = %d", vaddr, size); + + if (!vaddr || !size || !offset) { + VTRACE("invalid parameters"); + return false; + } + + arg.type = PSB_GTT_MAP_TYPE_VIRTUAL; + arg.page_align = gttAlign; + arg.vaddr = (unsigned long)vaddr; + arg.size = size; + + Drm *drm = Hwcomposer::getInstance().getDrm(); + ret = drm->writeReadIoctl(DRM_PSB_GTT_MAP, &arg, sizeof(arg)); + if (ret == false) { + ETRACE("gtt mapping failed"); + return false; + } + + VTRACE("offset = %#x", arg.offset_pages); + *offset = arg.offset_pages; + return true; +} + +bool TngGrallocBufferMapper::gttUnmap(void *vaddr) +{ + struct psb_gtt_mapping_arg arg; + bool ret; + + ATRACE("vaddr = %p", vaddr); + + if (!vaddr) { + ETRACE("invalid parameter"); + return false; + } + + arg.type = PSB_GTT_MAP_TYPE_VIRTUAL; + arg.vaddr = (unsigned long)vaddr; + + Drm *drm = Hwcomposer::getInstance().getDrm(); + ret = drm->writeIoctl(DRM_PSB_GTT_UNMAP, &arg, sizeof(arg)); + if (ret == false) { + ETRACE("gtt unmapping failed"); + return false; + } + + return true; +} + +bool TngGrallocBufferMapper::map() +{ + void *vaddr[SUB_BUFFER_MAX]; + uint32_t size[SUB_BUFFER_MAX]; + int gttOffsetInPage = 0; + bool ret; + int err; + int i; + + CTRACE(); + // get virtual address + err = mIMGGrallocModule.getCpuAddress(&mIMGGrallocModule, + (buffer_handle_t)mClonedHandle, + vaddr, + size); + if (err) { + ETRACE("failed to map. err = %d", err); + return false; + } + + for (i = 0; i < SUB_BUFFER_MAX; i++) { + // skip gtt mapping for empty sub buffers + if (!vaddr[i] || !size[i]) + continue; + + // map to gtt + ret = gttMap(vaddr[i], size[i], 0, >tOffsetInPage); + if (!ret) { + VTRACE("failed to map %d into gtt", i); + break; + } + + mCpuAddress[i] = vaddr[i]; + mSize[i] = size[i]; + mGttOffsetInPage[i] = gttOffsetInPage; + // TODO: set kernel handle + mKHandle[i] = 0; + } + + if (i == SUB_BUFFER_MAX) { + return true; + } + + // error handling + for (i = 0; i < SUB_BUFFER_MAX; i++) { + if (mCpuAddress[i]) { + gttUnmap(mCpuAddress[i]); + } + } + + err = mIMGGrallocModule.putCpuAddress(&mIMGGrallocModule, + (buffer_handle_t)mClonedHandle); + return false; +} + +bool TngGrallocBufferMapper::unmap() +{ + int i; + int err; + + CTRACE(); + + for (i = 0; i < SUB_BUFFER_MAX; i++) { + if (mCpuAddress[i]) + gttUnmap(mCpuAddress[i]); + + mGttOffsetInPage[i] = 0; + mCpuAddress[i] = 0; + mSize[i] = 0; + } + + err = mIMGGrallocModule.putCpuAddress(&mIMGGrallocModule, + (buffer_handle_t)mClonedHandle); + if (err) { + ETRACE("failed to unmap. err = %d", err); + } + return err; +} + +buffer_handle_t TngGrallocBufferMapper::getKHandle(int subIndex) +{ + buffer_handle_t ret = GrallocBufferMapperBase::getKHandle(subIndex); + if (subIndex == 0 && ret == 0) { + if (mapKhandle()) + return mKHandle[subIndex]; + } + + return ret; +} + +bool TngGrallocBufferMapper::mapKhandle() +{ + // TODO: this is a complete hack and temporary workaround + // need support from DDK to map khandle + void *wsbmBufferObject = 0; + int ret = psbWsbmWrapTTMBuffer2((uint64_t)mHandle, &wsbmBufferObject); + if (ret != 0) { + ETRACE("Wrap ttm buffer failed!"); + return false; + } + + ret = psbWsbmCreateFromUB(wsbmBufferObject, mWidth * mHeight, mCpuAddress[0]); + if (ret != 0) { + ETRACE("Create from UB failed!"); + return false; + } + + mKHandle[0] = (buffer_handle_t)(unsigned long)psbWsbmGetKBufHandle(wsbmBufferObject); + psbWsbmUnReference(wsbmBufferObject); + return true; +} + +buffer_handle_t TngGrallocBufferMapper::getFbHandle(int subIndex) +{ + void *vaddr[SUB_BUFFER_MAX]; + uint32_t size[SUB_BUFFER_MAX]; + int err; + + CTRACE(); + + if (subIndex < 0 || subIndex >= SUB_BUFFER_MAX) { + return 0; + } + + // get virtual address + err = mIMGGrallocModule.getCpuAddress(&mIMGGrallocModule, + (buffer_handle_t)mClonedHandle, + vaddr, + size); + if (err) { + ETRACE("failed to map. err = %d", err); + return 0; + } + + return (buffer_handle_t)vaddr[subIndex]; +} + +void TngGrallocBufferMapper::putFbHandle() +{ + int err = mIMGGrallocModule.putCpuAddress(&mIMGGrallocModule, + (buffer_handle_t)mClonedHandle); + if (err) { + ETRACE("failed to unmap. err = %d", err); + } + return; + +} + +} // namespace intel +} // namespace android diff --git a/merrifield/ips/tangier/TngGrallocBufferMapper.h b/merrifield/ips/tangier/TngGrallocBufferMapper.h new file mode 100644 index 0000000..d72005e --- /dev/null +++ b/merrifield/ips/tangier/TngGrallocBufferMapper.h @@ -0,0 +1,52 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef TNG_GRALLOC_BUFFER_MAPPER_H +#define TNG_GRALLOC_BUFFER_MAPPER_H + +#include <BufferMapper.h> +#include <hal_public.h> +#include <common/GrallocBufferMapperBase.h> +#include <tangier/TngGrallocBuffer.h> + +namespace android { +namespace intel { + +class TngGrallocBufferMapper : public GrallocBufferMapperBase { +public: + TngGrallocBufferMapper(IMG_gralloc_module_public_t& module, + DataBuffer& buffer); + virtual ~TngGrallocBufferMapper(); +public: + bool map(); + bool unmap(); + buffer_handle_t getKHandle(int subIndex); + buffer_handle_t getFbHandle(int subIndex); + void putFbHandle(); +private: + bool gttMap(void *vaddr, uint32_t size, uint32_t gttAlign, int *offset); + bool gttUnmap(void *vaddr); + bool mapKhandle(); + +private: + IMG_gralloc_module_public_t& mIMGGrallocModule; + void* mBufferObject; + native_handle_t* mClonedHandle; +}; + +} // namespace intel +} // namespace android + +#endif /* TNG_GRALLOC_BUFFER_MAPPER_H */ diff --git a/merrifield/ips/tangier/TngOverlayPlane.cpp b/merrifield/ips/tangier/TngOverlayPlane.cpp new file mode 100644 index 0000000..d837015 --- /dev/null +++ b/merrifield/ips/tangier/TngOverlayPlane.cpp @@ -0,0 +1,218 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <math.h> +#include <HwcTrace.h> +#include <Drm.h> +#include <Hwcomposer.h> +#include <tangier/TngOverlayPlane.h> +#include <tangier/TngGrallocBuffer.h> + +namespace android { +namespace intel { + +TngOverlayPlane::TngOverlayPlane(int index, int disp) + : OverlayPlaneBase(index, disp), + mRotationBufProvider(NULL) +{ + CTRACE(); + + memset(&mContext, 0, sizeof(mContext)); +} + +TngOverlayPlane::~TngOverlayPlane() +{ + CTRACE(); +} + +bool TngOverlayPlane::flip(void *ctx) +{ + RETURN_FALSE_IF_NOT_INIT(); + + if (!DisplayPlane::flip(ctx)) + return false; + + mContext.type = DC_OVERLAY_PLANE; + mContext.ctx.ov_ctx.ovadd = 0x0; + mContext.ctx.ov_ctx.ovadd = (mBackBuffer[mCurrent]->gttOffsetInPage << 12); + mContext.ctx.ov_ctx.index = mIndex; + mContext.ctx.ov_ctx.pipe = mDevice; + mContext.ctx.ov_ctx.ovadd |= mPipeConfig; + mContext.ctx.ov_ctx.ovadd |= 0x1; + + // move to next back buffer + //mCurrent = (mCurrent + 1) % OVERLAY_BACK_BUFFER_COUNT; + + VTRACE("ovadd = %#x, index = %d, device = %d", + mContext.ctx.ov_ctx.ovadd, + mIndex, + mDevice); + + return true; +} + +bool TngOverlayPlane::reset() +{ + OverlayPlaneBase::reset(); + if (mRotationBufProvider) + mRotationBufProvider->reset(); + return true; +} + +void* TngOverlayPlane::getContext() const +{ + CTRACE(); + return (void *)&mContext; +} + +bool TngOverlayPlane::setDataBuffer(BufferMapper& mapper) +{ + if (OverlayPlaneBase::setDataBuffer(mapper) == false) { + return false; + } + + if (mIsProtectedBuffer) { + // Bit 0: Decryption request, only allowed to change on a synchronous flip + // This request will be qualified with the separate decryption enable bit for OV + mBackBuffer[mCurrent]->buf->OSTART_0Y |= 0x1; + mBackBuffer[mCurrent]->buf->OSTART_1Y |= 0x1; + } + + mContext.gtt_key = (uint64_t)mapper.getCpuAddress(0); + return true; +} + +bool TngOverlayPlane::initialize(uint32_t bufferCount) +{ + if (!OverlayPlaneBase::initialize(bufferCount)) { + ETRACE("failed to initialize OverlayPlaneBase"); + return false; + } + + // setup rotation buffer + mRotationBufProvider = new RotationBufferProvider(mWsbm); + if (!mRotationBufProvider || !mRotationBufProvider->initialize()) { + DEINIT_AND_RETURN_FALSE("failed to initialize RotationBufferProvider"); + } + return true; +} + +void TngOverlayPlane::deinitialize() +{ + DEINIT_AND_DELETE_OBJ(mRotationBufProvider); + OverlayPlaneBase::deinitialize(); +} + +bool TngOverlayPlane::rotatedBufferReady(BufferMapper& mapper, BufferMapper* &rotatedMapper) +{ + struct VideoPayloadBuffer *payload; + VideoPayloadBuffer buffer_info; + uint32_t format; + // only NV12_VED has rotated buffer + format = mapper.getFormat(); + + if (format != OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar && + format != OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar_Tiled && + format != HAL_PIXEL_FORMAT_NV12) { + ETRACE("Invalid video format %#x", format); + return false; + } + + payload = (struct VideoPayloadBuffer *)mapper.getCpuAddress(SUB_BUFFER1); + + if (payload == NULL && format == HAL_PIXEL_FORMAT_NV12) { + // need to populate buffer_info + void *p = mapper.getCpuAddress(SUB_BUFFER0); + if (!p) { + ETRACE("failed to get buffer user pointer"); + return false; + } + + bool ret = mRotationBufProvider->prepareBufferInfo(mapper.getWidth(), + mapper.getHeight(), + mapper.getStride().yuv.yStride, + &buffer_info, p); + if (ret == false) { + ETRACE("failed to prepare buffer info"); + return false; + } + payload = &buffer_info; + } + + // check payload + if (!payload) { + ETRACE("no payload found"); + return false; + } + + if (payload->force_output_method == FORCE_OUTPUT_GPU) { + ETRACE("Output method is not supported!"); + return false; + } + + if (payload->client_transform != mTransform || + mBobDeinterlace) { + payload->hwc_timestamp = systemTime(); + payload->layer_transform = mTransform; + if (!mRotationBufProvider->setupRotationBuffer(payload, mTransform)) { + ETRACE("failed to setup rotation buffer"); + return false; + } + } + + rotatedMapper = getTTMMapper(mapper, payload); + + return true; +} + +bool TngOverlayPlane::flush(uint32_t flags) +{ + RETURN_FALSE_IF_NOT_INIT(); + ATRACE("flags = %#x, type = %d, index = %d", flags, mType, mIndex); + + if (!(flags & PLANE_ENABLE) && !(flags & PLANE_DISABLE)) + return false; + + struct drm_psb_register_rw_arg arg; + memset(&arg, 0, sizeof(struct drm_psb_register_rw_arg)); + + if (flags & PLANE_DISABLE) + arg.plane_disable_mask = 1; + else if (flags & PLANE_ENABLE) + arg.plane_enable_mask = 1; + + arg.plane.type = DC_OVERLAY_PLANE; + arg.plane.index = mIndex; + arg.plane.ctx = (mBackBuffer[mCurrent]->gttOffsetInPage << 12); + // pipe select + arg.plane.ctx |= mPipeConfig; + + if (flags & PLANE_DISABLE) { + DTRACE("disabling overlay %d on device %d", mIndex, mDevice); + } + + // issue ioctl + Drm *drm = Hwcomposer::getInstance().getDrm(); + bool ret = drm->writeReadIoctl(DRM_PSB_REGISTER_RW, &arg, sizeof(arg)); + if (ret == false) { + WTRACE("overlay update failed with error code %d", ret); + return false; + } + + return true; +} + +} // namespace intel +} // namespace android diff --git a/merrifield/ips/tangier/TngOverlayPlane.h b/merrifield/ips/tangier/TngOverlayPlane.h new file mode 100644 index 0000000..9129965 --- /dev/null +++ b/merrifield/ips/tangier/TngOverlayPlane.h @@ -0,0 +1,55 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef TNG_OVERLAY_PLANE_H +#define TNG_OVERLAY_PLANE_H + +#include <utils/KeyedVector.h> +#include <hal_public.h> +#include <DisplayPlane.h> +#include <BufferMapper.h> +#include <common/Wsbm.h> +#include <common/OverlayPlaneBase.h> +#include <common/RotationBufferProvider.h> + +namespace android { +namespace intel { + +class TngOverlayPlane : public OverlayPlaneBase { + +public: + TngOverlayPlane(int index, int disp); + virtual ~TngOverlayPlane(); + + virtual bool flip(void *ctx); + virtual bool reset(); + virtual void* getContext() const; + + virtual bool initialize(uint32_t bufferCount); + virtual void deinitialize(); + virtual bool rotatedBufferReady(BufferMapper& mapper, BufferMapper* &rotatedMapper); +protected: + virtual bool setDataBuffer(BufferMapper& mapper); + virtual bool flush(uint32_t flags); + +protected: + struct intel_dc_plane_ctx mContext; + RotationBufferProvider *mRotationBufProvider; +}; + +} // namespace intel +} // namespace android + +#endif /* TNG_OVERLAY_PLANE_H */ diff --git a/merrifield/ips/tangier/TngPlaneManager.cpp b/merrifield/ips/tangier/TngPlaneManager.cpp new file mode 100644 index 0000000..d973aa9 --- /dev/null +++ b/merrifield/ips/tangier/TngPlaneManager.cpp @@ -0,0 +1,196 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <HwcTrace.h> +#include <tangier/TngPlaneManager.h> +#include <tangier/TngPrimaryPlane.h> +#include <tangier/TngSpritePlane.h> +#include <tangier/TngOverlayPlane.h> +#include <tangier/TngCursorPlane.h> + +namespace android { +namespace intel { + +TngPlaneManager::TngPlaneManager() + : DisplayPlaneManager() +{ + memset(&mZorder, 0, sizeof(mZorder)); +} + +TngPlaneManager::~TngPlaneManager() +{ +} + +bool TngPlaneManager::initialize() +{ + mSpritePlaneCount = 1; // Sprite D + mOverlayPlaneCount = 2; // Overlay A & C + mPrimaryPlaneCount = 3; // Primary A, B, C + mCursorPlaneCount = 3; + + return DisplayPlaneManager::initialize(); +} + +void TngPlaneManager::deinitialize() +{ + DisplayPlaneManager::deinitialize(); +} + +DisplayPlane* TngPlaneManager::allocPlane(int index, int type) +{ + DisplayPlane *plane = 0; + + switch (type) { + case DisplayPlane::PLANE_PRIMARY: + plane = new TngPrimaryPlane(index, index); + break; + case DisplayPlane::PLANE_SPRITE: + plane = new TngSpritePlane(index, 0); + break; + case DisplayPlane::PLANE_OVERLAY: + plane = new TngOverlayPlane(index, 0); + break; + case DisplayPlane::PLANE_CURSOR: + plane = new TngCursorPlane(index, index /*disp */); + break; + default: + ETRACE("unsupported type %d", type); + break; + } + if (plane && !plane->initialize(DisplayPlane::MIN_DATA_BUFFER_COUNT)) { + ETRACE("failed to initialize plane."); + DEINIT_AND_DELETE_OBJ(plane); + } + + return plane; +} + +bool TngPlaneManager::isValidZOrder(int dsp, ZOrderConfig& config) +{ + // check whether it's a supported z order config + int firstRGB = -1; + int lastRGB = -1; + int firstOverlay = -1; + int lastOverlay = -1; + + for (int i = 0; i < (int)config.size(); i++) { + const ZOrderLayer *layer = config[i]; + switch (layer->planeType) { + case DisplayPlane::PLANE_PRIMARY: + case DisplayPlane::PLANE_SPRITE: + if (firstRGB == -1) { + firstRGB = i; + lastRGB = i; + } else { + lastRGB = i; + } + break; + case DisplayPlane::PLANE_OVERLAY: + case DisplayPlane::PLANE_CURSOR: + if (firstOverlay == -1) { + firstOverlay = i; + lastOverlay = i; + } else { + lastOverlay = i; + } + break; + } + } + + if ((lastRGB < firstOverlay) || (firstRGB > lastOverlay)) { + return true; + } else { + VTRACE("invalid z order config. rgb (%d, %d) yuv (%d, %d)", + firstRGB, lastRGB, firstOverlay, lastOverlay); + return false; + } +} + +bool TngPlaneManager::assignPlanes(int dsp, ZOrderConfig& config) +{ + // probe if plane is available + int size = (int)config.size(); + for (int i = 0; i < size; i++) { + const ZOrderLayer *layer = config.itemAt(i); + if (!getFreePlanes(dsp, layer->planeType)) { + DTRACE("no plane available for dsp %d, type %d", dsp, layer->planeType); + return false; + } + } + + if (config.size() == 1 && config[0]->planeType == DisplayPlane::PLANE_SPRITE) { + config[0]->planeType == DisplayPlane::PLANE_PRIMARY; + } + + // allocate planes + for (int i = 0; i < size; i++) { + ZOrderLayer *layer = config.itemAt(i); + layer->plane = getPlaneHelper(dsp, layer->planeType); + if (layer->plane == NULL) { + // should never happen!! + ETRACE("failed to assign plane for type %d", layer->planeType); + return false; + } + // sequence !!!!! enabling plane before setting zorder + // see TngSpritePlane::enablePlane implementation!!!! + layer->plane->enable(); + } + + // setup Z order + for (int i = 0; i < size; i++) { + ZOrderLayer *layer = config.itemAt(i); + layer->plane->setZOrderConfig(config, &mZorder); + } + + return true; +} + +void* TngPlaneManager::getZOrderConfig() const +{ + return (void*)&mZorder; +} + +DisplayPlane* TngPlaneManager::getPlaneHelper(int dsp, int type) +{ + RETURN_NULL_IF_NOT_INIT(); + + if (dsp < 0 || dsp > IDisplayDevice::DEVICE_EXTERNAL) { + ETRACE("Invalid display device %d", dsp); + return 0; + } + + int index = dsp == IDisplayDevice::DEVICE_PRIMARY ? 0 : 1; + + if (type == DisplayPlane::PLANE_PRIMARY || + type == DisplayPlane::PLANE_CURSOR) { + return getPlane(type, index); + } else if (type == DisplayPlane::PLANE_SPRITE) { + return getAnyPlane(type); + } else if (type == DisplayPlane::PLANE_OVERLAY) { + // use overlay A for pipe A and overlay C for pipe B if possible + DisplayPlane *plane = getPlane(type, index); + if (plane == NULL) { + plane = getPlane(type, !index); + } + return plane; + } else { + ETRACE("invalid plane type %d", type); + return 0; + } +} + +} // namespace intel +} // namespace android + diff --git a/merrifield/ips/tangier/TngPlaneManager.h b/merrifield/ips/tangier/TngPlaneManager.h new file mode 100644 index 0000000..e6717bb --- /dev/null +++ b/merrifield/ips/tangier/TngPlaneManager.h @@ -0,0 +1,50 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef TNG_PLANE_MANAGER_H +#define TNG_PLANE_MANAGER_H + +#include <DisplayPlaneManager.h> +#include <linux/psb_drm.h> + +namespace android { +namespace intel { + +class TngPlaneManager : public DisplayPlaneManager { +public: + TngPlaneManager(); + virtual ~TngPlaneManager(); + +public: + virtual bool initialize(); + virtual void deinitialize(); + virtual bool isValidZOrder(int dsp, ZOrderConfig& config); + virtual bool assignPlanes(int dsp, ZOrderConfig& config); + // TODO: remove this API + virtual void* getZOrderConfig() const; + +protected: + DisplayPlane* allocPlane(int index, int type); + DisplayPlane* getPlaneHelper(int dsp, int type); + +private: + struct intel_dc_plane_zorder mZorder; +}; + +} // namespace intel +} // namespace android + + +#endif /* TNG_PLANE_MANAGER_H */ diff --git a/merrifield/ips/tangier/TngPrimaryPlane.cpp b/merrifield/ips/tangier/TngPrimaryPlane.cpp new file mode 100644 index 0000000..7d72f0f --- /dev/null +++ b/merrifield/ips/tangier/TngPrimaryPlane.cpp @@ -0,0 +1,167 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <HwcTrace.h> +#include <Drm.h> +#include <tangier/TngPrimaryPlane.h> +#include <tangier/TngGrallocBuffer.h> +#include <common/PixelFormat.h> + +namespace android { +namespace intel { + +TngPrimaryPlane::TngPrimaryPlane(int index, int disp) + : TngSpritePlane(index, disp) +{ + CTRACE(); + mType = PLANE_PRIMARY; + mForceBottom = true; + mAbovePrimary = false; +} + +TngPrimaryPlane::~TngPrimaryPlane() +{ + CTRACE(); +} + +void TngPrimaryPlane::setFramebufferTarget(buffer_handle_t handle) +{ + CTRACE(); + + // do not need to update the buffer handle + if (mCurrentDataBuffer != handle) + mUpdateMasks |= PLANE_BUFFER_CHANGED; + else + mUpdateMasks &= ~PLANE_BUFFER_CHANGED; + + // if no update then do Not need set data buffer + if (!mUpdateMasks) + return; + + // don't need to map data buffer for primary plane + mContext.type = DC_PRIMARY_PLANE; + mContext.ctx.prim_ctx.update_mask = SPRITE_UPDATE_ALL; + mContext.ctx.prim_ctx.index = mIndex; + mContext.ctx.prim_ctx.pipe = mDevice; + mContext.ctx.prim_ctx.linoff = 0; + mContext.ctx.prim_ctx.stride = align_to((4 * align_to(mPosition.w, 32)), 64); + mContext.ctx.prim_ctx.pos = 0; + mContext.ctx.prim_ctx.size = + ((mPosition.h - 1) & 0xfff) << 16 | ((mPosition.w - 1) & 0xfff); + mContext.ctx.prim_ctx.surf = 0; + mContext.ctx.prim_ctx.contalpa = 0; + + mContext.ctx.prim_ctx.cntr = PixelFormat::PLANE_PIXEL_FORMAT_BGRA8888; + mContext.ctx.prim_ctx.cntr |= 0x80000000; + + mCurrentDataBuffer = handle; +} + +bool TngPrimaryPlane::enablePlane(bool enabled) +{ + RETURN_FALSE_IF_NOT_INIT(); + + struct drm_psb_register_rw_arg arg; + memset(&arg, 0, sizeof(struct drm_psb_register_rw_arg)); + if (enabled) { + arg.plane_enable_mask = 1; + } else { + arg.plane_disable_mask = 1; + } + arg.plane.type = DC_PRIMARY_PLANE; + arg.plane.index = mIndex; + arg.plane.ctx = 0; + + // issue ioctl + Drm *drm = Hwcomposer::getInstance().getDrm(); + bool ret = drm->writeReadIoctl(DRM_PSB_REGISTER_RW, &arg, sizeof(arg)); + if (ret == false) { + WTRACE("primary enabling (%d) failed with error code %d", enabled, ret); + return false; + } + + return true; + +} + +bool TngPrimaryPlane::setDataBuffer(buffer_handle_t handle) +{ + if (!handle) { + setFramebufferTarget(handle); + return true; + } + + TngGrallocBuffer tmpBuf(handle); + uint32_t usage; + bool ret; + + ATRACE("handle = %#x", handle); + + usage = tmpBuf.getUsage(); + if (GRALLOC_USAGE_HW_FB & usage) { + setFramebufferTarget(handle); + return true; + } + + // use primary as a sprite + ret = DisplayPlane::setDataBuffer(handle); + if (ret == false) { + ETRACE("failed to set data buffer"); + return ret; + } + + mContext.type = DC_PRIMARY_PLANE; + return true; +} + +void TngPrimaryPlane::setZOrderConfig(ZOrderConfig& zorderConfig, + void *nativeConfig) +{ + if (!nativeConfig) { + ETRACE("Invalid parameter, no native config"); + return; + } + + mForceBottom = false; + + int primaryIndex = -1; + int overlayIndex = -1; + // only consider force bottom when overlay is active + for (size_t i = 0; i < zorderConfig.size(); i++) { + DisplayPlane *plane = zorderConfig[i]->plane; + if (plane->getType() == DisplayPlane::PLANE_PRIMARY) + primaryIndex = i; + if (plane->getType() == DisplayPlane::PLANE_OVERLAY) { + overlayIndex = i; + } + } + + // if has overlay plane which is below primary plane + if (overlayIndex > primaryIndex) { + mForceBottom = true; + } + + struct intel_dc_plane_zorder *zorder = + (struct intel_dc_plane_zorder *)nativeConfig; + zorder->forceBottom[mIndex] = mForceBottom ? 1 : 0; +} + +bool TngPrimaryPlane::assignToDevice(int disp) +{ + return true; +} + +} // namespace intel +} // namespace android diff --git a/merrifield/ips/tangier/TngPrimaryPlane.h b/merrifield/ips/tangier/TngPrimaryPlane.h new file mode 100644 index 0000000..22dbbd3 --- /dev/null +++ b/merrifield/ips/tangier/TngPrimaryPlane.h @@ -0,0 +1,40 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef TNG_PRIMARY_PLANE_H +#define TNG_PRIMARY_PLANE_H + +#include <tangier/TngSpritePlane.h> + +namespace android { +namespace intel { + +class TngPrimaryPlane : public TngSpritePlane { +public: + TngPrimaryPlane(int index, int disp); + virtual ~TngPrimaryPlane(); +public: + bool setDataBuffer(buffer_handle_t handle); + void setZOrderConfig(ZOrderConfig& config, void *nativeConfig); + bool assignToDevice(int disp); +private: + void setFramebufferTarget(buffer_handle_t handle); + bool enablePlane(bool enabled); +}; + +} // namespace intel +} // namespace android + +#endif /* TNG_PRIMARY_PLANE_H */ diff --git a/merrifield/ips/tangier/TngSpritePlane.cpp b/merrifield/ips/tangier/TngSpritePlane.cpp new file mode 100644 index 0000000..c4e7e2c --- /dev/null +++ b/merrifield/ips/tangier/TngSpritePlane.cpp @@ -0,0 +1,215 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <HwcTrace.h> +#include <Hwcomposer.h> +#include <BufferManager.h> +#include <tangier/TngSpritePlane.h> +#include <common/PixelFormat.h> + +namespace android { +namespace intel { + +TngSpritePlane::TngSpritePlane(int index, int disp) + : SpritePlaneBase(index, disp) +{ + CTRACE(); + memset(&mContext, 0, sizeof(mContext)); +} + +TngSpritePlane::~TngSpritePlane() +{ + CTRACE(); +} + +bool TngSpritePlane::setDataBuffer(BufferMapper& mapper) +{ + int bpp; + int srcX, srcY; + int dstX, dstY, dstW, dstH; + uint32_t spriteFormat; + uint32_t stride; + uint32_t linoff; + uint32_t planeAlpha; + + CTRACE(); + + // setup plane position + dstX = mPosition.x; + dstY = mPosition.y; + dstW = mPosition.w; + dstH = mPosition.h; + + checkPosition(dstX, dstY, dstW, dstH); + + // setup plane format + if (!PixelFormat::convertFormat(mapper.getFormat(), spriteFormat, bpp)) { + ETRACE("unsupported format %#x", mapper.getFormat()); + return false; + } + + // setup stride and source buffer crop + srcX = mapper.getCrop().x; + srcY = mapper.getCrop().y; + stride = mapper.getStride().rgb.stride; + linoff = srcY * stride + srcX * bpp; + + // setup plane alpha + if ((mBlending == HWC_BLENDING_PREMULT) && (mPlaneAlpha == 0)) { + planeAlpha = mPlaneAlpha | 0x80000000; + } else { + // disable plane alpha to offload HW + planeAlpha = 0; + } + + // unlikely happen, but still we need make sure linoff is valid + if (linoff > (stride * mapper.getHeight())) { + ETRACE("invalid source crop"); + return false; + } + + // update context + mContext.type = DC_SPRITE_PLANE; + mContext.ctx.sp_ctx.index = mIndex; + mContext.ctx.sp_ctx.pipe = mDevice; + // none blending and BRGA format layer,set format to BGRX8888 + if (mBlending == HWC_BLENDING_NONE && spriteFormat == PixelFormat::PLANE_PIXEL_FORMAT_BGRA8888) + mContext.ctx.sp_ctx.cntr = PixelFormat::PLANE_PIXEL_FORMAT_BGRX8888 + | 0x80000000; + else + mContext.ctx.sp_ctx.cntr = spriteFormat | 0x80000000; + mContext.ctx.sp_ctx.linoff = linoff; + mContext.ctx.sp_ctx.stride = stride; + mContext.ctx.sp_ctx.surf = mapper.getGttOffsetInPage(0) << 12; + mContext.ctx.sp_ctx.pos = (dstY & 0xfff) << 16 | (dstX & 0xfff); + mContext.ctx.sp_ctx.size = + ((dstH - 1) & 0xfff) << 16 | ((dstW - 1) & 0xfff); + mContext.ctx.sp_ctx.contalpa = planeAlpha; + mContext.ctx.sp_ctx.update_mask = SPRITE_UPDATE_ALL; + mContext.gtt_key = (uint64_t)mapper.getCpuAddress(0); + + VTRACE("cntr = %#x, linoff = %#x, stride = %#x," + "surf = %#x, pos = %#x, size = %#x, contalpa = %#x", + mContext.ctx.sp_ctx.cntr, + mContext.ctx.sp_ctx.linoff, + mContext.ctx.sp_ctx.stride, + mContext.ctx.sp_ctx.surf, + mContext.ctx.sp_ctx.pos, + mContext.ctx.sp_ctx.size, + mContext.ctx.sp_ctx.contalpa); + return true; +} + +void* TngSpritePlane::getContext() const +{ + CTRACE(); + return (void *)&mContext; +} + +bool TngSpritePlane::enablePlane(bool enabled) +{ + RETURN_FALSE_IF_NOT_INIT(); + + struct drm_psb_register_rw_arg arg; + memset(&arg, 0, sizeof(struct drm_psb_register_rw_arg)); + if (enabled) { + arg.plane_enable_mask = 1; + } else { + arg.plane_disable_mask = 1; + } + arg.plane.type = DC_SPRITE_PLANE; + arg.plane.index = mIndex; + arg.plane.ctx = 0; + + // issue ioctl + Drm *drm = Hwcomposer::getInstance().getDrm(); + bool ret = drm->writeReadIoctl(DRM_PSB_REGISTER_RW, &arg, sizeof(arg)); + if (ret == false) { + WTRACE("sprite enabling (%d) failed with error code %d", enabled, ret); + return false; + } + + Hwcomposer& hwc = Hwcomposer::getInstance(); + DisplayPlaneManager *pm = hwc.getPlaneManager(); + void *config = pm->getZOrderConfig(); + if (config != NULL) { + struct intel_dc_plane_zorder *zorder = (struct intel_dc_plane_zorder *)config; + zorder->abovePrimary = 0; + } + + return true; + +} + +bool TngSpritePlane::isDisabled() +{ + RETURN_FALSE_IF_NOT_INIT(); + + struct drm_psb_register_rw_arg arg; + memset(&arg, 0, sizeof(struct drm_psb_register_rw_arg)); + + if (mType == DisplayPlane::PLANE_SPRITE) + arg.plane.type = DC_SPRITE_PLANE; + else + arg.plane.type = DC_PRIMARY_PLANE; + + arg.get_plane_state_mask = 1; + arg.plane.index = mIndex; + arg.plane.ctx = 0; + + // issue ioctl + Drm *drm = Hwcomposer::getInstance().getDrm(); + bool ret = drm->writeReadIoctl(DRM_PSB_REGISTER_RW, &arg, sizeof(arg)); + if (ret == false) { + WTRACE("plane state query failed with error code %d", ret); + return false; + } + + return arg.plane.ctx == PSB_DC_PLANE_DISABLED; +} + +void TngSpritePlane::setZOrderConfig(ZOrderConfig& zorderConfig, + void *nativeConfig) +{ + if (!nativeConfig) { + ETRACE("Invalid parameter, no native config"); + return; + } + + mAbovePrimary = false; + + int primaryIndex = -1; + int spriteIndex = -1; + // only consider force bottom when overlay is active + for (size_t i = 0; i < zorderConfig.size(); i++) { + DisplayPlane *plane = zorderConfig[i]->plane; + if (plane->getType() == DisplayPlane::PLANE_PRIMARY) + primaryIndex = i; + if (plane->getType() == DisplayPlane::PLANE_SPRITE) { + spriteIndex = i; + } + } + + // if has overlay plane which is below primary plane + if (spriteIndex > primaryIndex) { + mAbovePrimary = true; + } + + struct intel_dc_plane_zorder *zorder = + (struct intel_dc_plane_zorder *)nativeConfig; + zorder->abovePrimary = mAbovePrimary ? 1 : 0; +} +} // namespace intel +} // namespace android diff --git a/merrifield/ips/tangier/TngSpritePlane.h b/merrifield/ips/tangier/TngSpritePlane.h new file mode 100644 index 0000000..1c6bcdc --- /dev/null +++ b/merrifield/ips/tangier/TngSpritePlane.h @@ -0,0 +1,48 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef TNG_SPRITE_PLANE_H +#define TNG_SPRITE_PLANE_H + +#include <utils/KeyedVector.h> +#include <hal_public.h> +#include <Hwcomposer.h> +#include <BufferCache.h> +#include <DisplayPlane.h> + +#include <common/SpritePlaneBase.h> + +namespace android { +namespace intel { + +class TngSpritePlane : public SpritePlaneBase { +public: + TngSpritePlane(int index, int disp); + virtual ~TngSpritePlane(); +public: + virtual void* getContext() const; + virtual void setZOrderConfig(ZOrderConfig& config, void *nativeConfig); + virtual bool isDisabled(); +protected: + virtual bool setDataBuffer(BufferMapper& mapper); + virtual bool enablePlane(bool enabled); +protected: + struct intel_dc_plane_ctx mContext; +}; + +} // namespace intel +} // namespace android + +#endif /* TNG_SPRITE_PLANE_H */ diff --git a/merrifield/platforms/merrifield/Android.mk b/merrifield/platforms/merrifield/Android.mk new file mode 100644 index 0000000..98e1d3d --- /dev/null +++ b/merrifield/platforms/merrifield/Android.mk @@ -0,0 +1,139 @@ +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +LOCAL_PATH := $(call my-dir) + +# HAL module implemenation, not prelinked and stored in +# hw/<OVERLAY_HARDWARE_MODULE_ID>.<ro.product.board>.so +include $(CLEAR_VARS) + +LOCAL_PRELINK_MODULE := false +LOCAL_MODULE_RELATIVE_PATH := hw +LOCAL_SHARED_LIBRARIES := liblog libcutils libdrm \ + libwsbm libutils libhardware \ + libva libva-tpi libva-android libsync +LOCAL_SRC_FILES := \ + ../../common/base/Drm.cpp \ + ../../common/base/HwcLayer.cpp \ + ../../common/base/HwcLayerList.cpp \ + ../../common/base/Hwcomposer.cpp \ + ../../common/base/HwcModule.cpp \ + ../../common/base/DisplayAnalyzer.cpp \ + ../../common/base/VsyncManager.cpp \ + ../../common/buffers/BufferCache.cpp \ + ../../common/buffers/GraphicBuffer.cpp \ + ../../common/buffers/BufferManager.cpp \ + ../../common/devices/PhysicalDevice.cpp \ + ../../common/devices/PrimaryDevice.cpp \ + ../../common/devices/ExternalDevice.cpp \ + ../../common/observers/UeventObserver.cpp \ + ../../common/observers/VsyncEventObserver.cpp \ + ../../common/observers/SoftVsyncObserver.cpp \ + ../../common/observers/MultiDisplayObserver.cpp \ + ../../common/planes/DisplayPlane.cpp \ + ../../common/planes/DisplayPlaneManager.cpp \ + ../../common/utils/Dump.cpp + + +LOCAL_SRC_FILES += \ + ../../ips/common/BlankControl.cpp \ + ../../ips/common/HdcpControl.cpp \ + ../../ips/common/DrmControl.cpp \ + ../../ips/common/VsyncControl.cpp \ + ../../ips/common/PrepareListener.cpp \ + ../../ips/common/OverlayPlaneBase.cpp \ + ../../ips/common/SpritePlaneBase.cpp \ + ../../ips/common/PixelFormat.cpp \ + ../../ips/common/PlaneCapabilities.cpp \ + ../../ips/common/GrallocBufferBase.cpp \ + ../../ips/common/GrallocBufferMapperBase.cpp \ + ../../ips/common/TTMBufferMapper.cpp \ + ../../ips/common/DrmConfig.cpp \ + ../../ips/common/VideoPayloadManager.cpp \ + ../../ips/common/Wsbm.cpp \ + ../../ips/common/WsbmWrapper.c \ + ../../ips/common/RotationBufferProvider.cpp + +LOCAL_SRC_FILES += \ + ../../ips/tangier/TngGrallocBuffer.cpp \ + ../../ips/tangier/TngGrallocBufferMapper.cpp \ + ../../ips/tangier/TngOverlayPlane.cpp \ + ../../ips/tangier/TngPrimaryPlane.cpp \ + ../../ips/tangier/TngSpritePlane.cpp \ + ../../ips/tangier/TngDisplayQuery.cpp \ + ../../ips/tangier/TngPlaneManager.cpp \ + ../../ips/tangier/TngDisplayContext.cpp \ + ../../ips/tangier/TngCursorPlane.cpp + + +LOCAL_SRC_FILES += \ + PlatfBufferManager.cpp \ + PlatFactory.cpp + +LOCAL_C_INCLUDES := $(addprefix $(LOCAL_PATH)/../../../, $(SGX_INCLUDES)) \ + $(call include-path-for, frameworks-native)/media/openmax \ + $(TARGET_OUT_HEADERS)/khronos/openmax \ + $(call include-path-for, opengl) \ + $(call include-path-for, libhardware_legacy)/hardware_legacy \ + prebuilts/intel/vendor/intel/hardware/prebuilts/$(REF_DEVICE_NAME)/rgx \ + prebuilts/intel/vendor/intel/hardware/prebuilts/$(REF_DEVICE_NAME)/rgx/include \ + vendor/intel/hardware/PRIVATE/widi/libhwcwidi/ \ + system/core \ + system/core/libsync/include \ + $(TARGET_OUT_HEADERS)/drm \ + $(TARGET_OUT_HEADERS)/libdrm \ + $(TARGET_OUT_HEADERS)/libdrm/shared-core \ + $(TARGET_OUT_HEADERS)/libwsbm/wsbm \ + $(TARGET_OUT_HEADERS)/libttm \ + $(TARGET_OUT_HEADERS)/libva + +LOCAL_C_INCLUDES += $(LOCAL_PATH) \ + $(LOCAL_PATH)/../../include \ + $(LOCAL_PATH)/../../include/pvr/hal \ + $(LOCAL_PATH)/../../common/base \ + $(LOCAL_PATH)/../../common/buffers \ + $(LOCAL_PATH)/../../common/devices \ + $(LOCAL_PATH)/../../common/observers \ + $(LOCAL_PATH)/../../common/planes \ + $(LOCAL_PATH)/../../common/utils \ + $(LOCAL_PATH)/../../ips/ \ + $(LOCAL_PATH)/ + + +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := hwcomposer.$(TARGET_BOARD_PLATFORM) +LOCAL_CFLAGS += -DLINUX + +ifeq ($(INTEL_WIDI_MERRIFIELD), true) +LOCAL_SRC_FILES += \ + ../../common/devices/VirtualDevice.cpp + + LOCAL_SHARED_LIBRARIES += libhwcwidi libbinder + LOCAL_CFLAGS += -DINTEL_WIDI_MERRIFIELD +endif + +ifeq ($(TARGET_HAS_MULTIPLE_DISPLAY),true) + LOCAL_SHARED_LIBRARIES += libmultidisplay libbinder + LOCAL_CFLAGS += -DTARGET_HAS_MULTIPLE_DISPLAY +endif + +LOCAL_COPY_HEADERS := ../../include/pvr/hal/hal_public.h +LOCAL_COPY_HEADERS_TO := pvr/hal + +ifneq ($(TARGET_BUILD_VARIANT),user) + LOCAL_CFLAGS += -DHWC_TRACE_FPS +endif + +include $(BUILD_SHARED_LIBRARY) + diff --git a/merrifield/platforms/merrifield/PlatFactory.cpp b/merrifield/platforms/merrifield/PlatFactory.cpp new file mode 100644 index 0000000..7530225 --- /dev/null +++ b/merrifield/platforms/merrifield/PlatFactory.cpp @@ -0,0 +1,108 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <HwcTrace.h> +#include <tangier/TngDisplayContext.h> +#include <tangier/TngPlaneManager.h> +#include <PlatfBufferManager.h> +#include <IDisplayDevice.h> +#include <PrimaryDevice.h> +#include <ExternalDevice.h> +#ifdef INTEL_WIDI_MERRIFIELD +#include <VirtualDevice.h> +#endif +#include <Hwcomposer.h> +#include <PlatFactory.h> +#include <common/VsyncControl.h> +#include <common/HdcpControl.h> +#include <common/BlankControl.h> +#include <common/PrepareListener.h> +#include <common/VideoPayloadManager.h> + + +namespace android { +namespace intel { + +PlatFactory::PlatFactory() +{ + CTRACE(); +} + +PlatFactory::~PlatFactory() +{ + CTRACE(); +} + +DisplayPlaneManager* PlatFactory::createDisplayPlaneManager() +{ + CTRACE(); + return (new TngPlaneManager()); +} + +BufferManager* PlatFactory::createBufferManager() +{ + CTRACE(); + return (new PlatfBufferManager()); +} + +IDisplayDevice* PlatFactory::createDisplayDevice(int disp) +{ + CTRACE(); + //when createDisplayDevice is called, Hwcomposer has already finished construction. + Hwcomposer &hwc = Hwcomposer::getInstance(); + + class PlatDeviceControlFactory: public DeviceControlFactory { + public: + virtual IVsyncControl* createVsyncControl() {return new VsyncControl();} + virtual IBlankControl* createBlankControl() {return new BlankControl();} + virtual IHdcpControl* createHdcpControl() {return new HdcpControl();} + }; + + switch (disp) { + case IDisplayDevice::DEVICE_PRIMARY: + return new PrimaryDevice(hwc, new PlatDeviceControlFactory()); + case IDisplayDevice::DEVICE_EXTERNAL: + return new ExternalDevice(hwc, new PlatDeviceControlFactory()); +#ifdef INTEL_WIDI_MERRIFIELD + case IDisplayDevice::DEVICE_VIRTUAL: + return new VirtualDevice(hwc); +#endif + default: + ETRACE("invalid display device %d", disp); + return NULL; + } +} + +IDisplayContext* PlatFactory::createDisplayContext() +{ + CTRACE(); + return new TngDisplayContext(); +} + +IVideoPayloadManager *PlatFactory::createVideoPayloadManager() +{ + return new VideoPayloadManager(); +} + +Hwcomposer* Hwcomposer::createHwcomposer() +{ + CTRACE(); + Hwcomposer *hwc = new Hwcomposer(new PlatFactory()); + return hwc; +} + +} //namespace intel +} //namespace android diff --git a/merrifield/platforms/merrifield/PlatFactory.h b/merrifield/platforms/merrifield/PlatFactory.h new file mode 100644 index 0000000..73d6eea --- /dev/null +++ b/merrifield/platforms/merrifield/PlatFactory.h @@ -0,0 +1,43 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef MOOFPLATFORMFACTORY_H_ +#define MOOFPLATFORMFACTORY_H_ + +#include <IPlatFactory.h> + + +namespace android { +namespace intel { + +class PlatFactory : public IPlatFactory { +public: + PlatFactory(); + virtual ~PlatFactory(); + + virtual DisplayPlaneManager* createDisplayPlaneManager(); + virtual BufferManager* createBufferManager(); + virtual IDisplayDevice* createDisplayDevice(int disp); + virtual IDisplayContext* createDisplayContext(); + virtual IVideoPayloadManager *createVideoPayloadManager(); + +}; + +} //namespace intel +} //namespace android + + +#endif /* MOOFPLATFORMFACTORY_H_ */ diff --git a/merrifield/platforms/merrifield/PlatfBufferManager.cpp b/merrifield/platforms/merrifield/PlatfBufferManager.cpp new file mode 100644 index 0000000..1dd72c6 --- /dev/null +++ b/merrifield/platforms/merrifield/PlatfBufferManager.cpp @@ -0,0 +1,77 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <HwcTrace.h> +#include <PlatfBufferManager.h> +#include <tangier/TngGrallocBuffer.h> +#include <tangier/TngGrallocBufferMapper.h> + +namespace android { +namespace intel { + +PlatfBufferManager::PlatfBufferManager() + : BufferManager() +{ + +} + +PlatfBufferManager::~PlatfBufferManager() +{ + +} + +bool PlatfBufferManager::initialize() +{ + return BufferManager::initialize(); +} + +void PlatfBufferManager::deinitialize() +{ + BufferManager::deinitialize(); +} + +DataBuffer* PlatfBufferManager::createDataBuffer(gralloc_module_t *module, + buffer_handle_t handle) +{ + return new TngGrallocBuffer(handle); +} + +BufferMapper* PlatfBufferManager::createBufferMapper(gralloc_module_t *module, + DataBuffer& buffer) +{ + if (!module) + return 0; + + return new TngGrallocBufferMapper(*(IMG_gralloc_module_public_t*)module, + buffer); +} + +bool PlatfBufferManager::blit(buffer_handle_t srcHandle, buffer_handle_t destHandle, + const crop_t& destRect, bool filter, bool async) + +{ + IMG_gralloc_module_public_t *imgGrallocModule = (IMG_gralloc_module_public_t *) mGrallocModule; + if (imgGrallocModule->Blit(imgGrallocModule, srcHandle, + destHandle, + destRect.w, destRect.h, destRect.x, + destRect.y, filter, 0, async)) { + ETRACE("Blit failed"); + return false; + } + return true; +} + +} // namespace intel +} // namespace android diff --git a/merrifield/platforms/merrifield/PlatfBufferManager.h b/merrifield/platforms/merrifield/PlatfBufferManager.h new file mode 100644 index 0000000..822f484 --- /dev/null +++ b/merrifield/platforms/merrifield/PlatfBufferManager.h @@ -0,0 +1,43 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef PLATF_BUFFER_MANAGER_H +#define PLATF_BUFFER_MANAGER_H + +#include <BufferManager.h> + +namespace android { +namespace intel { + +class PlatfBufferManager : public BufferManager { +public: + PlatfBufferManager(); + virtual ~PlatfBufferManager(); + +public: + bool initialize(); + void deinitialize(); + +protected: + DataBuffer* createDataBuffer(gralloc_module_t *module, buffer_handle_t handle); + BufferMapper* createBufferMapper(gralloc_module_t *module, + DataBuffer& buffer); + bool blit(buffer_handle_t srcHandle, buffer_handle_t destHandle, + const crop_t& destRect, bool filter, bool async); +}; + +} +} +#endif /* PLATF_BUFFER_MANAGER_H */ diff --git a/merrifield/platforms/merrifield_plus/Android.mk b/merrifield/platforms/merrifield_plus/Android.mk new file mode 100644 index 0000000..2f5f63d --- /dev/null +++ b/merrifield/platforms/merrifield_plus/Android.mk @@ -0,0 +1,143 @@ +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +LOCAL_PATH := $(call my-dir) + +# HAL module implemenation, not prelinked and stored in +# hw/<OVERLAY_HARDWARE_MODULE_ID>.<ro.product.board>.so +include $(CLEAR_VARS) + +LOCAL_PRELINK_MODULE := false +LOCAL_MODULE_RELATIVE_PATH := hw +LOCAL_SHARED_LIBRARIES := liblog libcutils libdrm \ + libwsbm libutils libhardware \ + libva libva-tpi libva-android libsync +LOCAL_SRC_FILES := \ + ../../common/base/Drm.cpp \ + ../../common/base/HwcLayer.cpp \ + ../../common/base/HwcLayerList.cpp \ + ../../common/base/Hwcomposer.cpp \ + ../../common/base/HwcModule.cpp \ + ../../common/base/DisplayAnalyzer.cpp \ + ../../common/base/VsyncManager.cpp \ + ../../common/buffers/BufferCache.cpp \ + ../../common/buffers/GraphicBuffer.cpp \ + ../../common/buffers/BufferManager.cpp \ + ../../common/devices/PhysicalDevice.cpp \ + ../../common/devices/PrimaryDevice.cpp \ + ../../common/devices/ExternalDevice.cpp \ + ../../common/observers/UeventObserver.cpp \ + ../../common/observers/VsyncEventObserver.cpp \ + ../../common/observers/SoftVsyncObserver.cpp \ + ../../common/observers/MultiDisplayObserver.cpp \ + ../../common/planes/DisplayPlane.cpp \ + ../../common/planes/DisplayPlaneManager.cpp \ + ../../common/utils/Dump.cpp + + +LOCAL_SRC_FILES += \ + ../../ips/common/BlankControl.cpp \ + ../../ips/common/HdcpControl.cpp \ + ../../ips/common/DrmControl.cpp \ + ../../ips/common/VsyncControl.cpp \ + ../../ips/common/PrepareListener.cpp \ + ../../ips/common/OverlayPlaneBase.cpp \ + ../../ips/common/SpritePlaneBase.cpp \ + ../../ips/common/PixelFormat.cpp \ + ../../ips/common/GrallocBufferBase.cpp \ + ../../ips/common/GrallocBufferMapperBase.cpp \ + ../../ips/common/TTMBufferMapper.cpp \ + ../../ips/common/DrmConfig.cpp \ + ../../ips/common/VideoPayloadManager.cpp \ + ../../ips/common/Wsbm.cpp \ + ../../ips/common/WsbmWrapper.c \ + ../../ips/common/RotationBufferProvider.cpp + +LOCAL_SRC_FILES += \ + ../../ips/tangier/TngGrallocBuffer.cpp \ + ../../ips/tangier/TngGrallocBufferMapper.cpp \ + ../../ips/tangier/TngDisplayQuery.cpp \ + ../../ips/tangier/TngDisplayContext.cpp + + +LOCAL_SRC_FILES += \ + ../../ips/anniedale/AnnPlaneManager.cpp \ + ../../ips/anniedale/AnnOverlayPlane.cpp \ + ../../ips/anniedale/AnnRGBPlane.cpp \ + ../../ips/anniedale/AnnCursorPlane.cpp \ + ../../ips/anniedale/PlaneCapabilities.cpp + + +LOCAL_SRC_FILES += \ + PlatfBufferManager.cpp \ + PlatFactory.cpp + + +LOCAL_C_INCLUDES := $(addprefix $(LOCAL_PATH)/../../../, $(SGX_INCLUDES)) \ + frameworks/native/include/media/openmax \ + $(TARGET_OUT_HEADERS)/khronos/openmax \ + frameworks/native/opengl/include \ + hardware/libhardware_legacy/include/hardware_legacy \ + prebuilts/intel/vendor/intel/hardware/prebuilts/$(REF_DEVICE_NAME)/rgx \ + prebuilts/intel/vendor/intel/hardware/prebuilts/$(REF_DEVICE_NAME)/rgx/include \ + vendor/intel/hardware/PRIVATE/widi/libhwcwidi/ \ + system/core \ + system/core/libsync/include \ + $(TARGET_OUT_HEADERS)/drm \ + $(TARGET_OUT_HEADERS)/libdrm \ + $(TARGET_OUT_HEADERS)/libdrm/shared-core \ + $(TARGET_OUT_HEADERS)/libwsbm/wsbm \ + $(TARGET_OUT_HEADERS)/libttm \ + $(TARGET_OUT_HEADERS)/libva + +LOCAL_C_INCLUDES += $(LOCAL_PATH) \ + $(LOCAL_PATH)/../../include \ + $(LOCAL_PATH)/../../include/pvr/hal \ + $(LOCAL_PATH)/../../common/base \ + $(LOCAL_PATH)/../../common/buffers \ + $(LOCAL_PATH)/../../common/devices \ + $(LOCAL_PATH)/../../common/observers \ + $(LOCAL_PATH)/../../common/planes \ + $(LOCAL_PATH)/../../common/utils \ + $(LOCAL_PATH)/../../ips/ \ + $(LOCAL_PATH)/ + + +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := hwcomposer.$(TARGET_BOARD_PLATFORM) +LOCAL_CFLAGS += -DLINUX + +#$(error local path is: $(LOCAL_C_INCLUDES)) +ifeq ($(INTEL_WIDI_MERRIFIELD), true) +LOCAL_SRC_FILES += \ + ../../common/devices/VirtualDevice.cpp + + LOCAL_SHARED_LIBRARIES += libhwcwidi libbinder + LOCAL_CFLAGS += -DINTEL_WIDI_MERRIFIELD +endif + +ifeq ($(TARGET_HAS_MULTIPLE_DISPLAY),true) + LOCAL_SHARED_LIBRARIES += libmultidisplay libbinder + LOCAL_CFLAGS += -DTARGET_HAS_MULTIPLE_DISPLAY +endif + +LOCAL_COPY_HEADERS := ../../include/pvr/hal/hal_public.h +LOCAL_COPY_HEADERS_TO := pvr/hal + +ifneq ($(TARGET_BUILD_VARIANT),user) + LOCAL_CFLAGS += -DHWC_TRACE_FPS +endif + +include $(BUILD_SHARED_LIBRARY) + diff --git a/merrifield/platforms/merrifield_plus/PlatFactory.cpp b/merrifield/platforms/merrifield_plus/PlatFactory.cpp new file mode 100644 index 0000000..095a4a1 --- /dev/null +++ b/merrifield/platforms/merrifield_plus/PlatFactory.cpp @@ -0,0 +1,106 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <HwcTrace.h> +#include <tangier/TngDisplayContext.h> +#include <anniedale/AnnPlaneManager.h> +#include <PlatfBufferManager.h> +#include <IDisplayDevice.h> +#include <PrimaryDevice.h> +#include <ExternalDevice.h> +#include <VirtualDevice.h> +#include <Hwcomposer.h> +#include <PlatFactory.h> +#include <common/VsyncControl.h> +#include <common/HdcpControl.h> +#include <common/BlankControl.h> +#include <common/PrepareListener.h> +#include <common/VideoPayloadManager.h> + + + +namespace android { +namespace intel { + +PlatFactory::PlatFactory() +{ + CTRACE(); +} + +PlatFactory::~PlatFactory() +{ + CTRACE(); +} + +DisplayPlaneManager* PlatFactory::createDisplayPlaneManager() +{ + CTRACE(); + return (new AnnPlaneManager()); +} + +BufferManager* PlatFactory::createBufferManager() +{ + CTRACE(); + return (new PlatfBufferManager()); +} + +IDisplayDevice* PlatFactory::createDisplayDevice(int disp) +{ + CTRACE(); + // when createDisplayDevice is called, Hwcomposer has already finished construction. + Hwcomposer &hwc = Hwcomposer::getInstance(); + class PlatDeviceControlFactory: public DeviceControlFactory { + public: + virtual IVsyncControl* createVsyncControl() {return new VsyncControl();} + virtual IBlankControl* createBlankControl() {return new BlankControl();} + virtual IHdcpControl* createHdcpControl() {return new HdcpControl();} + }; + + switch (disp) { + case IDisplayDevice::DEVICE_PRIMARY: + return new PrimaryDevice(hwc, new PlatDeviceControlFactory()); + case IDisplayDevice::DEVICE_EXTERNAL: + return new ExternalDevice(hwc, new PlatDeviceControlFactory()); +#ifdef INTEL_WIDI_MERRIFIELD + case IDisplayDevice::DEVICE_VIRTUAL: + return new VirtualDevice(hwc); +#endif + default: + ETRACE("invalid display device %d", disp); + return NULL; + } +} + +IDisplayContext* PlatFactory::createDisplayContext() +{ + CTRACE(); + return new TngDisplayContext(); +} + +IVideoPayloadManager * PlatFactory::createVideoPayloadManager() +{ + return new VideoPayloadManager(); +} + +Hwcomposer* Hwcomposer::createHwcomposer() +{ + CTRACE(); + Hwcomposer *hwc = new Hwcomposer(new PlatFactory()); + return hwc; +} + +} //namespace intel +} //namespace android diff --git a/merrifield/platforms/merrifield_plus/PlatFactory.h b/merrifield/platforms/merrifield_plus/PlatFactory.h new file mode 100644 index 0000000..73d6eea --- /dev/null +++ b/merrifield/platforms/merrifield_plus/PlatFactory.h @@ -0,0 +1,43 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef MOOFPLATFORMFACTORY_H_ +#define MOOFPLATFORMFACTORY_H_ + +#include <IPlatFactory.h> + + +namespace android { +namespace intel { + +class PlatFactory : public IPlatFactory { +public: + PlatFactory(); + virtual ~PlatFactory(); + + virtual DisplayPlaneManager* createDisplayPlaneManager(); + virtual BufferManager* createBufferManager(); + virtual IDisplayDevice* createDisplayDevice(int disp); + virtual IDisplayContext* createDisplayContext(); + virtual IVideoPayloadManager *createVideoPayloadManager(); + +}; + +} //namespace intel +} //namespace android + + +#endif /* MOOFPLATFORMFACTORY_H_ */ diff --git a/merrifield/platforms/merrifield_plus/PlatfBufferManager.cpp b/merrifield/platforms/merrifield_plus/PlatfBufferManager.cpp new file mode 100644 index 0000000..1dd72c6 --- /dev/null +++ b/merrifield/platforms/merrifield_plus/PlatfBufferManager.cpp @@ -0,0 +1,77 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <HwcTrace.h> +#include <PlatfBufferManager.h> +#include <tangier/TngGrallocBuffer.h> +#include <tangier/TngGrallocBufferMapper.h> + +namespace android { +namespace intel { + +PlatfBufferManager::PlatfBufferManager() + : BufferManager() +{ + +} + +PlatfBufferManager::~PlatfBufferManager() +{ + +} + +bool PlatfBufferManager::initialize() +{ + return BufferManager::initialize(); +} + +void PlatfBufferManager::deinitialize() +{ + BufferManager::deinitialize(); +} + +DataBuffer* PlatfBufferManager::createDataBuffer(gralloc_module_t *module, + buffer_handle_t handle) +{ + return new TngGrallocBuffer(handle); +} + +BufferMapper* PlatfBufferManager::createBufferMapper(gralloc_module_t *module, + DataBuffer& buffer) +{ + if (!module) + return 0; + + return new TngGrallocBufferMapper(*(IMG_gralloc_module_public_t*)module, + buffer); +} + +bool PlatfBufferManager::blit(buffer_handle_t srcHandle, buffer_handle_t destHandle, + const crop_t& destRect, bool filter, bool async) + +{ + IMG_gralloc_module_public_t *imgGrallocModule = (IMG_gralloc_module_public_t *) mGrallocModule; + if (imgGrallocModule->Blit(imgGrallocModule, srcHandle, + destHandle, + destRect.w, destRect.h, destRect.x, + destRect.y, filter, 0, async)) { + ETRACE("Blit failed"); + return false; + } + return true; +} + +} // namespace intel +} // namespace android diff --git a/merrifield/platforms/merrifield_plus/PlatfBufferManager.h b/merrifield/platforms/merrifield_plus/PlatfBufferManager.h new file mode 100644 index 0000000..822f484 --- /dev/null +++ b/merrifield/platforms/merrifield_plus/PlatfBufferManager.h @@ -0,0 +1,43 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#ifndef PLATF_BUFFER_MANAGER_H +#define PLATF_BUFFER_MANAGER_H + +#include <BufferManager.h> + +namespace android { +namespace intel { + +class PlatfBufferManager : public BufferManager { +public: + PlatfBufferManager(); + virtual ~PlatfBufferManager(); + +public: + bool initialize(); + void deinitialize(); + +protected: + DataBuffer* createDataBuffer(gralloc_module_t *module, buffer_handle_t handle); + BufferMapper* createBufferMapper(gralloc_module_t *module, + DataBuffer& buffer); + bool blit(buffer_handle_t srcHandle, buffer_handle_t destHandle, + const crop_t& destRect, bool filter, bool async); +}; + +} +} +#endif /* PLATF_BUFFER_MANAGER_H */ diff --git a/merrifield/test/Android.mk b/merrifield/test/Android.mk new file mode 100644 index 0000000..b0a262e --- /dev/null +++ b/merrifield/test/Android.mk @@ -0,0 +1,32 @@ +# Build the unit tests, +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE := nv12_ved_test + +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := \ + nv12_ved_test.cpp \ + +LOCAL_SHARED_LIBRARIES := \ + libEGL \ + libGLESv2 \ + libbinder \ + libcutils \ + libgui \ + libstlport \ + libui \ + libutils \ + +LOCAL_C_INCLUDES := \ + bionic \ + $(call include-path-for, libstdc++) \ + $(call include-path-for, gtest) \ + $(call include-path-for, stlport) + +# Build the binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE) +# to integrate with auto-test framework. +include $(BUILD_EXECUTABLE) + + diff --git a/merrifield/test/my_640x480.nv12 b/merrifield/test/my_640x480.nv12 new file mode 100644 index 0000000..d8a0c34 --- /dev/null +++ b/merrifield/test/my_640x480.nv12 @@ -0,0 +1 @@ +"&&%# *6BTjstx{zywxxwtnlptw|}|{{|{ywvuvyyxyz|}{zxz~~||||}~~~||€~}{z{}~€~}€~~ƒ‚}}~‡‰‡~€‚€}}‚†ˆ‡†ˆˆ‡†ƒ‚ƒƒ‚€~‚…††‰ŠŠ‰ˆˆ‡†……†‡ˆŠˆ„‚ƒ„…††††ˆˆ‡‡…ƒ„‰Ž‰†…„‚„†…ƒ…‡ˆ‰Š‰ˆˆŠŠˆ……‡ŠŒ‹Šˆ………†‡‡†ƒ…ˆ‰Š‹Š‰‰Š‰ˆ‡ˆŠ‹Š‰‰‰‡†‹‹‡‡Š‹‹Š˜g- !#$'+/01147889?DB=4) *7BShpminqrtwyz|€‚€}xxƒM"!$$#!'4BUhqtx{zyxxxxsnkotwz|{{{{{yxwuvxyxyz{|{{{{€}{zzz|~~}||~}|{zz{}}}~|{‚€~}}}~~…‡…€}€‚€~€}}„……†‰‰‡…ƒ‚ƒƒ€~„…‡ˆ‰‰ˆ‡††…ƒƒ…†‡‰ˆ…ƒ„……†‡††‡†††„ƒ„‰Œ‰‡†……ƒ„…ƒ‚„‡ˆ‰‰ˆ†‡‰‰‡…†‡ŠŒ‹‰ˆ†……………†…ƒ†ˆˆˆ‰‰ˆˆ‰ˆ‡†ˆ‰ŠŠ‰‰ˆ†…‰ŒŠˆˆ‰ŠŠ—n1 "#&*-/0148:9:>@A=7.#".;Qijjloqrtwxy{€€}yw€C #0BUeotx{zywwwwtnmquwyyz{{yxvwxwvxzz{{||}~~~€}zxwxz|}}|~€€€~}}}||~‚€€€~{{}€€~}}~~ƒ‚}€ƒƒƒƒƒ€~||}ƒ…†ˆ‰†ƒ‚‚ƒ‚~€ƒ†‡ˆˆˆˆ†…„……ƒƒ„…†ˆˆ†…†‡ˆ‰‰ˆ‡…„……„„†‰Šˆ†‡††„ƒƒ‚‚…‡‰‹Š‡…†ˆˆ‡‡‡‡ˆ‰‰‡‡‡……†…„†ˆˆˆˆ‰‰‰‰‰‰Šˆ‡‡ˆ‰Š‹‹Š‰‡†ˆŒŒŒ‰ˆˆˆˆ˜ˆR!#%&(*,.027=@>=?ADFGE<4*!(7Vghnqrsvwwvx}~~~z}z5 ,BVdmsy{zxvvvwtpnquvwxy{{ywwwyywwy{|}}}}~~}€|ywvwyz{|}~~~}}‚‚€~}yy{}~~~~~~~~„„ƒ…†„~}}~€ƒ…‡‡‡…ƒ‚„…‚€‚ƒ…†‡‡†…„„„„…„„ƒ„…‡ˆ‡††‡‡‡ˆ‡…ƒƒ„…„„…‡ˆ††‡††„ƒƒ„…†‡ˆŠ‰…ƒƒ†‡ˆ‰‰†††††‡ˆ†„…ƒ„‡ŠŠˆ†ˆ‰‰ˆˆ‰‰‰ˆˆˆˆˆŠ‹‰ˆ‡††‰ŠŠ‰ˆ‰ˆ‡œr $')))*+-028?A>>ADEFJLJHE;," '8P_mmpsvvusuwyz||z}z#+BWdnty{zxvwyyusrtuvwxyz{zyyzzywvx|~}}~€€~{ywwy{|}~~~~~~~‚‚€~~~}|{|‚‚‚€€‚‚‚ƒ„„„†‡†„ƒƒ‚€€‚„‡ˆˆ‡……„………ƒƒ„„ƒ„ˆ‰ˆ†„„„„„……‡†……‡ˆˆ‡†††ˆˆ‡…„…………††‡ˆˆ‰Šˆ†„ƒ„†‡‡‡‡‰‰‡††ˆ‰Š‹Š‡†…†‡ˆŠ‰‡†††ˆ‹‰ˆ‡ˆŠ‹ŠŠŠ‹‹ŠŠ‰ˆˆ‰‹Š‰‰ˆ‡‰ŠŠ‰Š‹Š‡‡£ "$#$'+.--/00349?AABFIHIMRRRTQG5##)G`moswxvtuvvxz{y}y(@Ucovyzxwwy{zwuuuvvvxxz{{{}~|yvtvz~~~|{|~}yxxxxz}~~€~~}}}|}}}~€€~€~~‚ƒƒ‚ƒ„………„ƒ‚ƒ…†††‡†„ƒ„†‡ˆ‡†††………††………‚„‰‹‰†„…††„„„‡ˆ‡……†‡†„„†‰‰‡†‡†…„…†‡‡ˆ‰‹‹‰‡……†††††††‡†‡ˆˆˆ‰‰‰†……†††‰Šˆˆˆˆ‰‰‡‡‡ˆŠ‹Š‰Š‹‹Šˆˆˆˆˆ‰‰ˆˆˆ‡‰ŠŠ‰ŠŒŠ‡‡œ’ $(('(*--0211348;?BEIJIKNRVXXYUK:(!'H\juvxxvvvuuxzx|v%:Pasuwxwwxyzzxvvwxxwxyz|||}}|xuuvy|}}}}{z{zwtuwwwz}~€€€€~~}}|||~„„„‚€‚‚ƒƒ‚ƒ„…………„~‚„†‡‡ˆ‡‡‡ˆˆ††††‡††‡‰ˆ‡ˆ‰‡†‰‹Š‡„…‡‡…„…†ˆˆ†……†‡†…‡ˆˆ‡‰‰‰‡††‡‡‰Š‹‹‹Š‰ˆˆˆ‡†††‡‡†…†‡‡‡‡ˆˆ‡ˆˆˆ‡‡‹ŽŒ‹Š‹‹‹‰‡‡‰Š‹Š‰Š‹‹‹‰‰‰‰ˆ‰‰ˆ‡ˆˆˆ‰Š‹Œ‹‰‰Š˜˜-!""""%(+++,-/2321358;@DGIKLMNQVZZZYTMA0""0C]wwuyzzxvvy{zx 1GYjprttuuuuvuttvwxwyz{||{{zzxwxwy|}|{{zyxwuttvwwz}|}€€€€€~}||z|}~€ƒ„ƒ‚€~€€‚‚‚‚}}€ƒ……„…†††††…„„…………†‡‡‡ˆŠˆ‡ˆŠ‰‡„…………„ƒ„…†…„…‡ˆ‰ˆ‡†††ˆˆˆ‡……„„‡‰‰ˆˆ‰‰ˆ‰ˆ‡……†‡‡…„„……„ƒ…‡ˆˆ‰‰‡ˆŒŽ‹‰ˆ‰Š‹ˆ……‡‰‹‹ŠŠ‹‹Œ‹Š‰‰ˆŠŠˆ†‡ˆˆ‡ˆŠ‹‰‰Š‹˜˜[ !$&%%'(,..../1112469<@CEHLMNNQUXXXXXVSL9&#.=Xlmuz{{yxz|{s+CXfmqttssssssstuxyyz{||{yyyyxxy{}~}{zzzyyyyxxxz}~~€‚‚€€‚~~~}}~€‚ƒ‚€‚‚‚ƒ„„‚~€‚‚†‡†„ƒ„…„………„ƒƒ„……††‡ˆˆŠ‰ˆ‰Š‰ˆ††…„„„„……†……†‰‹‹‹Šˆ‡ˆ‰ˆ‡†…„„„†ˆˆ‡‡ˆˆ‡‡††……†‡‡‡††‡‰ˆ††‡ˆ‰ŒŒ‰‰ŒŠˆˆ‰‹Œ‰…†‰‹ŒŒŒŒ‹ŠŠ‹ŒŠŠ‹‹‰‰Š‹‹Š‹‘š€L !"#&())+-0122102357:;=?BDEJNNOQSUUUXZ[\]\T@,%+8Rrx|~}|||{†i&=Sbkorssrstsrstuwyyz{{{ywxyyyz{}€}{{z{{{|~|zz|~~‚‚‚‚€‚~|}‚‚€‚‚‚ƒ‡‡ƒ~|€ƒ„„„„ƒ‚†ˆˆ†„€‚ƒƒ€‚ƒƒ„…………†‡†‡‡‡‡ˆˆ‡†††………„ƒ„‡‰ŠŠˆˆ‡‡‡‡†……„„„„…‡‡……††…ƒ‚„†…†ˆ‰‰ˆ††ˆ‰ˆ†††ˆ‹Š‡‡ˆˆ‡†‡‡‰‰ˆ†‡‰‹ŒŒ‹ŒŽ‹Š‰ŠŽŽŒ‹ŒŒ‹‹ŠŠ‹‹‹™—j!#$%'))*,/1124423468;=?@BCFJLMPSTSRSVZ\]^^^WF/&+2Unqx~}|{yxc!7Pclpsuutttttuvvwxxy{{yxxyxx{}}}~€€~||||}}‚|}€€‚‚„„ƒ‚€€}~€€‚„ƒƒ…ˆˆ…‚†ˆˆ‡†…„‡Š‰‡…‚€‚‚€‚‚„„ƒƒƒ……„„„…‡‰Š‰ˆ‰ŠŠˆ‡†…‡‰Š‰ˆ††‡ˆ‡‡††‡ˆˆ‰‡‡‡ˆ†††…ƒ‚ƒ…‡‡‰‹Œ‹Šˆ††‰‰ˆˆ‡ˆ‰‰‡‡‡ˆ‡††‡‡ˆŠŠ‰Š‹ŒŒŒŒŽŽŒŠŒŽŒŒ‹Š‹Œ‹Œž€ #'*+,,,/23224446789<@BCEGHJMORTUTRRUWZ\\\_`YI4',9]ou}~}|yyb0Kbmsvwwvuttvwxxwwwxzywwz{yy{|}||~~}||}~€}{}~€„„‚€~}~€€€~}~€ƒ†‡…‚‚ƒ†‰‰‡†……‡‡†……ƒ‚‚ƒ……‚~€‚„…ƒ€ƒ…†ƒ‚„†‰‹Šˆˆ‹Œ‹‰‰ŠŠ‡…„…‡ˆ‡††ˆˆŠŠŠ‰‡‡‡††††……†‡ˆˆˆ‹‹‰‡…†ˆ‰‰ˆ‡‡‡†‡ˆ‰‰‡…„„†‰Š‰ˆˆŠ‹ŒŒŽŒ‹ŒŽŽŒ‹ŒŒ‹‹ŒŠ‰Š‹Š‹‹‹¥‘!',--,,033233369;<=@BCCFHIKORUVVUTTUVX[[[^a_YL819Kgw}~}}}zze /H`nuyzxuvuuxyyyxxxz|{yz~~|||}~~}~~~}}}~€€€|z|~€€€‚ƒƒ€€~|~~~€ƒ„ƒ‚‚€€€€‚…†„„…„…‡‡…„„…†…‚„„ƒ‚„…ˆ‡ƒ€€ƒ†‡„‚…‡‡„‚ƒ…†‰‹Šˆˆ‹ŽŽŽŽŽŠ…ƒƒ…ˆ‰‡†ˆŠ‰ŠŠ‹Š‰ˆ‡†‡‡‡†‡‡ˆˆˆˆŠŒŽŒŠˆˆ‰ŒŠ‡†‡ˆ‰ŠŠ‰ˆ‡‡…†‰‹‹‰ˆ‰Š‹ŒŽŒŽŽŽŒŒ‹ŠŠ‹Š‰‰‰Ÿ1"(,-/013458978;?BCCEDCBGKLNRTVVUUWXYYY\`abbbb^P=2>Zq€|}~zxe!!.E\otyzvrtuuxyyyyy{}~|z{~~}}}~€~~~~~}}~~~~{z|}}€€€€‚‚€€€~€‚……ƒ……„‚€€„†……„„ƒ„ƒ€€‚„„ƒ‚‚ƒ„††ƒ‚ƒƒ„‡‡„ƒ‡‰ˆ†„…ˆŠ‹‹ˆ‡‡ŠŽŽŒ‹†‚ƒ…‡ˆ†‡ˆˆ‡ˆˆˆˆˆ‡†…†……†‡‡‡††ˆŠ‹ŒŠ‰ˆ‰‹Œ‰‡†ˆ‹‹Šˆ†‡‰‰‡‡‰Š‹‹‹‹‹‹Š‹ŽŒŽ‹ŒŽ‹ŒŽ‹Š‰ˆˆˆ‡^!"$(*,.25558<<;=@CDEDEB@?DIKLPRSTTVXZ\\[\`cddefe_O72>Qs{v{}ywd +AXkrwxtqrstvxyzzz|}|{zz|||~‚‚€€~}}}~}|}~~€~~€€ƒ„‚‚‚‚‚…ˆ†„†‡†…„ƒ„‚€‚…‡†…ƒƒ‚€€‚„…„€‚„„„„„„ƒƒ…†††††…„…ˆŠŠ‰‡‡Š‹‹Šˆˆˆ‹ŒŒ‹ŠŠŠˆ…‚ƒ†‡ˆ‰‰ŠŠ‰ˆˆˆ‰‰ˆ‡††††…†‡‡‡††‰Š‰ŠŽŒ‹Š‰‰‰ˆˆ‰‹‹Šˆ†‰Ž‹ŠŠŠŒŒ‹‹‹ŒŽŒŽŒŽŽŒ‰‰ˆ‰Šˆ‹™|2!"$'*,/2579<>>?BDDEFFECAACGILOOPSUWZ]^]]]^adeffec]K4..Wtwz{xvb':N^jqrollmnoruxxy{zxyzyxxy|ƒƒ‚€~}}€€~}}}~€€~€‚„…„„„ƒƒƒ‚‚ƒ…†…ƒ„……†……†„‚ƒ………„ƒ‚€€ƒ„…‡‡ƒ€…‡‡†…„„ƒ„……††…„…††ˆ‰Š‰‡‡‰ŠŠ‰ˆ‰Š‹‹Šˆˆˆ‡…„„‡‰Š‰‰Š‹ŠŠ‰‡‡‰Š‰††††‡‡‡†…††‡ˆˆ‡ˆ‹ŒŠˆ‡ˆ‰‰ˆ‰ŠŠˆ‡ŠŒŒŠŠ‰‰Š‹Œ‹Š‹ŒŽŒ‹ŒŒŒŽŽŒ‹ŽŽ‹ˆ‡ˆŠ‹‰†’‘d!"#(+.0379;=>@@AEGHHIHEDCDGJKNNPRTWZ\^^^^^^`bdddcbZG.+0O~yzvt`$2ER\ab`_`ceehpwxyvvvxyyxyy{ƒ„„ƒ‚‚‚‚€~~€‚ƒ~}}~‚‚‚€‚„…………„ƒ‚ƒ…………ƒ‚ƒ„……†‡‡†…††„„„…ƒƒ„……„‡ˆˆ‡‡ˆˆ‡‡‡†…†‡†††„…‡ˆˆˆ‰ˆ‡‡‰Š‹‰‡‡ˆŠ‹‹‹‰‰ˆ†„„†‰ŠŠ‰‰Š‹‹‹Šˆ‡‰‹‹‰ˆˆˆˆˆˆˆˆˆˆ‰Š‰ˆˆ‰‹Œ‹ˆˆ‰Š‰ˆ‰Š‹‹Š‹Œ‹Š‹Š‰‰Š‹‹‹ŽŒŒŒŒŽŒ‹ŒŽŒ‰ˆˆŠ‹Šˆ£‚"%'(+.15:===>ACBAFJKKKJIHHGKNNOPPRUXZ]___`a`__`bdedbX>,/%„„{wwf!,8BILJJMRUUSRXaefkptwxzyyz|‚‚‚€€€€€}~€‚‚~|||ƒƒ‚‚€€‚ƒ……„ƒ‚ƒ„…„ƒƒ‚‚‚‚„††††‡†ƒ‚ƒƒ‚‚ƒ„…†…†ˆ‰‰‡…„„…††……†‡†††††‡‡ˆˆ‡‡‡ˆ‰‹Œ‰……‡‰Š‹‹Šˆ‡…„†‰Šˆˆ‡ˆˆˆˆŠŠˆ†‡ˆ‰ˆˆ‰ˆ‡‡‡‰‰‰ˆ‰ŒŒ‹‰ˆ‰ŠŠ‹Šˆˆ‰‰ˆ††ˆ‰‹‹ŠŠ‹ŠŠŠŠ‰‰Š‹ŒŽŒŠ‰Š‹Œ‹ŒŒŠŠ‹ŒŒ‹ŠˆˆŠ‹Š‰‰© $)*+-.27<@?>?BDCADGGHGGIKKKNPOPRRRUXZ\^]_`cb_\]`dfed_P:+(rŠ{tu_ ").34215:<;:9:DKYfotxz|{z{}€€€€€~~€€}}~}‚„„…„ƒƒƒ‚ƒ„……„ƒ„„„ƒƒ‚ƒ€„††‡††…ƒƒ„…………‡ˆ‰ŠŠŠˆ…ƒƒƒ„………‡ˆ††‡‰‰‰ˆˆ‰‰ˆ‰ˆŠŒŒ‰†…‡ŠŒŒ‰‡††‰‹ŒŠ‡‡†††‡‰‹‹‰‡ˆ‰‰‰‰ŠŠˆ‡‡‰ŠŠŠŠŒŽŒŒ‹‰‰ŠŠ‰‰‰‡††ˆŠ‹‹Š‹Œ‹Š‹Œ‹‹‹ŒŽŒŠ‰ŠŒŒ‹‹Œ‹Œ‹ˆ‰‹‹Š‰Š¡’ !!%*,,--28>AAACEGECBCDFEEKOQRSSRQTVTVWY\]]_bdc`]]`egffc\K21;g}stW $7Uhqwz{}{zz|€~~€~~€~~ƒ………„„ƒ„„††„ƒ‚ƒ‚‚‚€€‚‚€ƒ„……‡†„ƒ‚‚ƒ„„„ƒ„†ˆ‰‰‰ˆ†„ƒ„ƒƒ…††‡†……‡‰ŠŠˆ‡ˆ‡†‡ˆŠŒ‹ˆ‡†‡ˆŠŒ‹ˆ††‡Š‹‹‰…„„……‡Š‹Šˆ‡ˆŠŠˆˆ‡‡†††‰‰Š‹ŠŠŽ‹‰ˆŠŠ‰ˆˆˆˆ‰‹ŒŠŠŠŠ‹‹ŒŒŽŽŒ‹ŒŽŽŒŒŒŽŽŒˆˆ‹ŒŠŠ‹¡–D# $*----18<>ABCEFFDBBCFGHLPRSSTSSUVUUWZ\\\^acbba`adeefebXC4:Iqqm]3Sitz|||{zz}€~~}}~€‚‚€€€ƒƒ€~}‚………ƒƒ„…………„‚‚‚ƒƒ€€€‚„††……‡‡„„……ƒ„„ƒ‚ƒ„†‡ˆ‰ˆ†…ƒƒ„„…‡ˆˆ‡‡†……‡Š‰‡…………‡‰‹‹Š‰ŠŠˆ†‡Š‹‹‹ŠŠŠŠŠˆ…………„‡‰‹Š‰‰Š‹ŒŠˆ††‡‡‡‰‰Š‹Š‰Œ‘ŽŒŠ‰ˆˆˆˆŠŒŒŒŽŒ‰‰‰‰‹Ž‘‘ŽŽ‘‘Ž‘‘ŽŒ‹ŒŒ‰‹ŽŽŒ‹‹˜—m?#)-./148:=ACDDDFGEEEHJLMORRRSTTUUTTW\\\^_abbeedddddeeeaT?//jnmb,Omx|~||zyy~}||}€~€€ƒƒ€}}~€‚ƒ€ƒ‚‚‚€€‚„…„‚€€‚…‡†„„…†…†ˆˆ†…„ƒ‚…†‡ˆˆ†„ƒƒ„………††††‡†„ƒ…‡†ƒ‚‚ƒ„‡‰Š‰ˆ‡‰Šˆ…†ˆŠ‹‹‹‹ˆ‡ˆˆ‡ˆ‰ŒŠ‰Š‹‹Š‹Š‰‰ˆ†„†ˆˆ‡‰‰‰ŠŠ‰Š‘‹‰ˆˆˆ‰ŠŒŒŒŒŠ††‡‰‹ŒŽŽŽ‘‘‘ŽŽ‘’‹ŠŠŒŽŒ‹ŒŽŽŒŠŠ–…^ !$*-.1469;=BEFFFIKJHGIKMNPQQPRTSSUTTWZZ\_`abdhhffecceedd^L-3^jmc&Cjx|~|||{|€~||}€€€€~~€„„€€€~€€€ƒ……„‚‚€€„ˆ†„…†‡†‡ˆˆ‡††…ƒ„‡‡‡ˆˆ†……†‡ˆ‡†††……†…„ƒƒ……„ƒƒ„…‡ˆˆ‡ˆ‰Š‹‰‡ˆ‹‹‰ŠŒŠˆŠ‹Š‹ŒŽŽŒŒŒ‹‹‹‰‡†‡‡ˆ‰‰ŠŠ‰‰Š‹‰‰Ž‘‹Š‰Š‹ŒŽŽŽŒŒŠˆˆ‰Š‹ŽŽŽ‘’’‘‘‘‘‘Ž’ŒŽŽŽŽ‹ˆˆ”u "&*-14469<?CDDFJJIJKKLNPPOPSTSSSSUTW[[Z[^`beedefgfddfeddbR1-E`ke'Dbqy}|zz{}}}|zy}}}||}}}}}~€‚‚‚‚€}}}{{}€€„…„‚€€‚‚‚‚„……†‡‡†‡ˆˆ‡ˆ‰ˆ††††……†ˆ‰‡„„„„„„„„ƒƒƒ‚‚„…†‡††ˆ‹Œ‹‰‰ˆ‡ˆˆˆˆˆˆˆˆ‰‰Š‹‹‹‹Š‹‹Š‡„…‡‡††‡‡ˆ‰ŠŠ‰‰‰ŠŒŒ‹‹Š‰ŠŠŠŠŠŠŠŠ‹Ž‹‹ŒŠŒŽŒŒŽŽŽŽŽŽŽŒŽŒŽŽŽŽŽŽŒŒŠ‰‰Œª‰ "$'*-256:=?ADEGKMKJMNNOQRQOOSTTTSTWY[]]\\]_bddcfgfeeggfddcW=19Shl%A\nvzyxyz{{{zyz~}{{}~~~~€€‚‚‚€~~}{|‚‚…„‚€€€~€ƒƒ‚…ˆ†…††ˆ‰ŠŠ‰‰‰‡……††…„†‡ˆ‡…„……„„„…„„ƒ‚‚ƒ„……‡ˆ‰‹Ž‹‰‰ˆ‡ˆŠ‹‹‰‰ŠŠŠ‰Š‹ŠŠŠŠˆ‰‹‹Š‡‡ˆˆˆˆˆ‰Š‹‹‹ŒŒ‹‹‹ŒŒŒŒ‹Š‰Š‰‰ŠŒŒŒŽ’‘‘ŒŽŽŽŽŽŽŽŒŒ‹ŠŠ‰¡—!$%)-059;=?@CEGJOPQRSSRSSSROOSVVVUWZ]^__^___adccfhgfhjiheecYG4)3`n#?Zjvywvyzzzzzz{€}{{|}~~~~€€~€€€€~~€ƒ„ƒ€~€‚€€‚…„‚†ˆ†……†‡ˆ‰ˆˆˆˆ‡…………„ƒƒ„„„………†……†††‡‡†…„………†‰‹‹ŒŒŠ‰‰‡‡‰ŠŠ‰†‡Š‹Š‰Š‰ˆ†„„†ŠŒŒ‹‹ŠŠŠŠ‰ˆŠŒŒ‹‹‹‹ŒŽŒ‹ŒŒŒŠ‹ŒŽŽŒŒŽŽ‘ŽŽ‘’’‘Ž‹‰‡”š. #$%)-048:<?BEFHMPQSVWUTRRTSPPSVVVVVY\^_`^____bcdffefijihfebZP<$/UT !>[jswvvy{zyyyz|~~|{|}~€‚ƒƒƒ€€€€€‚ƒ‚€~‚ƒ„„„€‚…„ƒƒ„‡‡„„‡ˆ†……†ˆˆˆˆ‡‡ˆˆ‡†……ƒ‚‚„……†††‡‡‡ˆŠŠŠ‰‡†ˆˆ‰‹Œ‹Š‹‹‹Šˆˆ‰Š‰‡…†Š‹Š‰Š‰‡…‚ƒˆŽŽŒ‹Š‹‹‹ŠŠŠ‹‹ŒŒ‹Š‹ŽŽŽŽŠŠŒ‹ŒŽŽŽ‹ŠŠ‹ŽŽŽ‘’’‘‘‘’’’ŽŽŽŽ‹ˆ†”›K!"#$%(+/26:=AEGHKQSQRUUTRQRUVTSUUTUWWYZ\_ba`aaaaceeeeegihhfd`[UE&*OR% !"#"! 9fksxwxz{zzz{|}~~~{z|~€‚„„‚€€€€€€~ƒ‚€€ƒ„…„ƒ‚…†…„ƒ„†‡……††ƒƒ„†‰ˆ‡‡‡††‡‡†…„‚ƒ…††‡ˆˆˆˆˆ‰‰‰ŠŠˆ‡ˆŠ‹‹‰ˆ‰Š‹‹‹Š‰ŠŠˆ††‡ŠŠ‰‰Š‰ˆ‡…†ŠŽŽŒ‹ŠŠ‹Œ‹‰ˆˆ‰ŠŠ‰‰Š‹ŒŒŽŽŽŽŽŽŒ‰Š‹‹‰ŠŽŽŒŠ‹‘‘’’““’ŽŽŽŽŒ‹‰ˆ–š`( !"$&)-16:?CGJKMSURQSRQPQQTVVVVUTVYZ[Z[\^_`abbbcddefghiiifc`\YK,.RW9 "$%%$#"/`otyxx{|}}}~€€}|~€‚‚‚„ƒ€~€€€‚‚ƒƒƒ„……†‡†……„…‡‡†……†ˆˆ‡‡…‚‚„†ˆˆ†††……†‡‡†…„„„†‡ˆˆ‰Š‹Œ‹Šˆˆˆ‰‹ŒŒ‰‰‹‹ˆˆ‰‰‰‹Œ‹Ž‹‰ˆŠŒŒŠŠŠ‹‹‹‰‰ŽŒ‹‹‹‹Œ‹ŠŠ‰‰‰‹‹‹‹‹‹ŽŽŒŠŠ‹‹‰‰‘Ž‘’““’‘‘’’‘’’‘‘’““‘Œ‹Š‰‰ŠœyJ !"#$(+.39>CGIKMNSVTTVTRRSSTUVWXXVVY]^]\\\_abccefgghijjijifc`^[O22SYA #%$$$#2Zntxxx{~~€€~€‚‚€~}~‚„ƒ‚ƒ„„„†‡‡†††…†‡…ƒƒ…………†ˆˆ‡†ƒ‚ƒ„„„„††……††‡‰‰‰ˆ‡‡ˆ‰Š‹‹‹ŠŠ‰ˆ††ˆ‹ŒŒŠ‰‰ŠŠˆˆˆ‡‡‰ŠŒŽŒŠ‰‹ŒŒ‹ŠŠ‹‹Š‰ŠŽŽ‹‰ˆˆŠ‹‹‹ŒŒ‹‹‹ŒŒŒŒŽŒŒŒ‰ˆ‹Ž‘‘ŽŒŒŽŽŽŽŽŽ‘’““’’’’‘‘‘Ž‹‰ˆˆŠŠ›…X "&(')+-17=CGIJKMQTTWYXVUVVUUUUVYWUX[]]^]]_``abeghiiikjiigb_^^\Q87T[F#!#$$" /Xiswxy{€~€‚€€€€}}~}€‚ƒƒ„†…„…†……„ƒƒ…‡…€‚„„†‡ˆ‡……„ƒ‚‚‚‚‚„†‡‡‡ˆˆ‰ŠŠ‰ˆ‡‡‡‰Š‹‹Š‰Š‹Š‡†‡‰Š‹Œ‹ˆˆˆ‰‰‰‰ˆˆ‰‹ŒŠŠŠŒŽ‹Š‰‹Š‰‰‹ŽŽ‹‰ˆ††ˆ‰‰ŠŒŒŒŽŒŒŽŽŽŽŽŽ‹‰‹ŽŽŽ‘““”“’’’‘’’ŽŒ‹ŠŠŠ˜\ "'++)),05<ADHIKMQSTWZZYYYYYXVTSVXUW[\^___`aaabehiijjlljif_[^_\T@<QXF'!!""!0Tgqvy{}€~~€~€€€€~~~~}{|~„„………†…‚‚€ƒ‚„„†ˆ‡„„…††„‚‚‚ƒ…††‡ˆ‰‰‰ˆ‡…†‡‡†ˆŠŠ‰‰ˆŠŒŠ‡†‡ˆ‰Œ‰‡ˆ‰‰Š‹Š‰‹‹Š‰ˆ‡ˆŠŒŽ‹Š‰‰‰ˆ‰‰‹Šˆˆ††ˆŠŠŠ‹ŒŒŒŒŽŽŽŽŒŒ‘ŒŒŽŽŽŽŽŽ‘‘“””“ŽŽŽŽŽ‹ŠŒ“{U!#',-*)-26;=?EILORSUWXXYZZYXYXVVWWVY\\^`__`abccehihhillkie_^`a^VF=JQD( 3Vhsy}€‚ƒ€~~~€€€€€€€€~}{z|ƒ††††‡ˆ‡ƒ€€~‚…†……ˆ‡…„‡ˆˆ†…„„†ˆˆ‡‡‡‡††…„„‡Š‹ŠŠŠ‹‹Š‰‰‹‹‰‡‡ˆŠ‘ŽŠ‰‰ŠŠŠ‹‹ŒŒŠˆˆ‰ˆ‰‹Ž‹ŠŠŠ‰ŠŒŒŠ‰ˆ‰‰‡‰ŽŒŽŽŽŽŽŽŽ‘Ž‘‘Ž‘’‘‘‘‘‘’“•••’‘‘’’‘ŽŽ‘ŽŒŒŠŠŽuU $',//-.379:=@FLOPRSVXYYZ[[YWY[[[ZYY[\]`a``abdffghhhhjklkiebbde`WH<AG?(5Xjx{~€€ƒ‚€€€‚‚‚€€€~|yz~…‡ˆ‡†…††ƒ€€‚…ˆˆ…„††„…‡‰‰ˆ‡††ˆŠŠ‡…††…„ƒ‚‚‡ŒŽŒŠŠŒ‹ˆ‡‰ŠŠ‰ˆ‰Œ‰‰ŠŠ‰‰Š‹ŒŒŠ‰‰‹‹ŠŒŽŽŒ‹‰‰‹Œ‰‡‡ˆ‹ŒŠŠŽŽŽŒŒŽŽŽŽŽŽŽ‘‘’“‘’“’’’’‘’”•–•“’’’“‘ŽŒŽ‘‘ŒŠ‹‹ŠŽnN""&)-011248::=BHMPPPQUXZ[[\]\ZZ[[[[[\[Z[^_^__`cghhhhhhjkjjhfcbdd`XK@@D>(5Xny|~€€ƒƒƒƒ‚ƒ‚€€‚ƒ‚€€~}ƒ…‡ˆˆ‡…„„„„ƒ‚‚‚ƒƒ„†ˆŠ‰…ƒ……ƒƒ…ˆ‰‰Š‰ˆˆŠ‹‰‡ˆ‰‡†……ƒ†‰ŠŠŠŠŒŒ‰ˆ‹Œ‹‹ŽŽ‹ˆ‰‹Œ‹‹‹ŒŒŒ‹Š‰ŠŠ‰ŒŽŽŒ‹ŒŽŒ‰‰Š‹Œ‹ŒŽŒŒŽŒ‹ŒŽŽŽŽ‘‘Ž’’’““’‘’“”“’“”””–—•”“’“””’ŽŽŒ‰Š‹‹ŒdC $'*,034348;<?CHMPQRTXZ[\\]^]][[[[\]]\[\]\\]__bfhiiihhjjjjihecdc_YOFCD<%5Yoz|€€ƒƒƒ„„ƒ‚€€„„}}~~~€‚ƒ„††…ƒƒ‚‚ƒƒ„„„„…†‡ˆ‰Šˆ…„……ƒƒ†‡‰‰‰ˆ†‡‰‰ˆ‰‰‡††…„…‡‡‡‡ˆŠ‹‹Š‹ŒŒ‹ŠŒŠˆ‡‰ŒŒŒŒŒŒŒŒŠˆ‰ŠŠŒŒŽŒ‹ŒŒŽŒ‹Š‹‹Œ‹ŒŒŒ‹‹‹ŒŒŽŽŽŽŒŠ‹ŒŒŒŽŽŒ’’’’’’““””“‘‘“••••“’’““”“ŽŽ‘‘Ž‹‰ˆŠŠ“_> %()+045347;<>BGKOSVZ\]]]]^_^]\\^^^^^^]^^][\^_acehihfgghjkjhedc`\XSKFC7!5^r{}€‚‚‚ƒ„„„„‚€€ƒƒ}||}}‚„„………„‚€‚‚ƒƒ„„„„…‡ˆ‰ŠŠ‰‡†††„ƒ„†‡‰ŠŠˆ‡‡ˆ‰‰‰‰ˆ‡†††‡ˆˆ‡‡ˆ‰Š‹‹ŒŠŠ‹ŽŒŠˆ‡‰ŒŽŽŒ‹‹ŒŽŒŒŒŠŠŒŽŒŠ‹Œ‹‹ŒŒ‹‹ŒŒŒŒŒŒŽŽŽŽŽ‘“““““”•”””“‘’•––•“’’”••”‘‘’’‘ŽŒŠŠŒ‹—‹V7!'*,.256568;<>DIKOTY^`^^_aaa_^^^``^]]]^_`a```abbdhigfhhikmjgfea^ZXTLGB/4^ow}ƒƒ‚‚ƒƒ‚€€ƒ„ƒ‚~‚ƒ„„ƒ‚€‚ƒ‚ƒƒƒ‚„†‡‡ˆˆ†………………††‡ˆŠŠŠŠ‰ˆˆˆˆ‰ˆ†………‡‰Šˆ‡ˆŠŠ‹‹‹Š‰ˆŠŽŽŠˆ†ˆŒŽ‘Œ‹‹ŽŒŒŠ‰ŠŽ‹‡ˆ‹‹‹ŒŽŽŒ‹‹‹ŒŽŽŽŒŒŒŽŽŽŽ““““““““”””“’‘“””””“’”””“’‘‘‘‘‘‹Š˜ƒI-"'+.146668:<<@FLMOTY\]\]`bcba``__^\[ZZZ\]_ababcccfgffhjklkhfge`[WVTOI=#2\my~€‚ƒ€~~~„……„‚‚‚€„…„ƒ€€‚„ƒ€‚ƒƒ„…‡ˆ‡†…ƒ‚ƒ„…†ˆˆ‡†‡‰‹Œ‹ŠŠŠ‰‰‰ˆ†……‡ˆŠ‹ˆ†ˆŒ‹Šˆˆ‰ŒŽŽŒ‰‡‡‰ŒŽŽŽŠ‰‹ŽŒŒŽŒ‹Ž‹ˆŠ‘Œ‹‹Š‹ŒŒŽ‘ŽŽŽŽŽ‘’““’‘‘‘‘‘’“’’’’’‘’””““’‘‘’“”“’““““’’’’’’’’ŽŽ‹Š‹‘•~L. #(-1466568;=>BIMOQTXYZYZ]`aa```_^\ZYYWXZ[]bcbbcedefeegikkigefe`ZVTROK8,_ox~€€€€€€~}}}|~‚…†…ƒ‚~}}€„…ƒ‚‚‚‚‚„„€‚„†‡ˆ‡…„‚‚€‚…‡‰‰ˆ†…‡‹ŒŠ‡ˆŠ‰‡‡†„„…‰ŠŠˆ……‡‹ŒŠ‰ˆˆ‰ŒŒŠˆ‡ˆ‹ŽŽ‹ˆ‡‰‹‹ŽŠŠŠŒŒŒ‹ŒŒ‹Œ‹‰‰‹ŠŒ‘Ž‹Š‹Š‹ŒŽŽ‹Œ‘‘ŽŽ‘‘“”””“’““’’’‘‘‘‘’“““”“‘‘“”‘‘““’’‘‘’’’‘ŽŽŽ‹Š‰ŠxJ+"'+/1232368<?DIKNPSUVXZZ[]_____^]\[[ZXX[\^bdbbcdeeggfefgghhfdc`[XSNLJ5"/\ox}~€€€€~}|zz|€„……„ƒ~~„„„„…„‚‚……ƒ‚ƒ…‡ˆ‰ˆ…ƒƒƒ‚‚ƒ†ˆ‰‰ˆ††‡ŠŒ‹ˆ‡ˆˆ‡……††‡ŠŒ‹ˆ†‡ˆ‰Š‰‰ŠŠ‹ŒŽŽ‹Š‰ŠŒŠˆˆ‰ŠŒŽŠ‰‰ŠŠ‹ŒŒ‹ŒŒŒŒ‹ŠŠŠŠŒŽŽŒ‹ŒŽŽŽŽŽ‘‘“““””””””””‘‘“’’““’““”””’‘’””’’’“““’’“’’‘‹‹‰…‰rG*!'*,-.01257:@FIKLNQUWZ\\\]___`_]]]\\[YZ]__cedddeffhkjihggghhfca^[UKFB.#%(+./.,,,-.//02465444420,)&#!"#$#"!"""""$&%$$(',A]nw|~€€€‚‚€}{yyz~‚„„……„‚‚ƒ‚‚ƒ„‡ˆ†ƒ‚„†„‚ƒƒƒ†‡‰Š‰†„ƒ„„„…‡‰‰‡†††‡‰‹Šˆ‡…†……†‡‡ˆŠŒ‹Š‰‡‡‡†…†‰‹ŒŽŽŒ‹‹‘ŽŒŒŒ‹‹‹Š‹Œ‘Šˆ‰‰‡ˆ‹ŒŠŠ‹ŒŒ‹ŠŠŠ‹‹Œ‹Š‹ŒŽŒ‹Œ‘ŽŽ‘’“’’““’’‘‘‘‘‘’””“““““’“••–•““““’‘‘“””’ŽŒŒŒ‹ŠŠ‹i@'!&))*,/13468=CGFHLPTX[]^^_`aaa`^]]]\[[[^^^_abdfghhhjnnmjihhhgdb`]VI=5%:=BGLOLFDEHJGFILNNNOQQSTRNID=9<CIJGB?ABCACFE@>CIKXeqw{}€‚ƒ€}zy{~‚…†ˆˆ†„………‚‚…ˆŠ‰†……†„‚ƒ„…‡‡ˆ‰Šˆ†„…†‡ˆˆ‰‰‡…†‡‡ˆˆˆˆ‡…„„…‡‡‡ˆ‹ŒŒ‹ˆ‡ˆˆ‡†‡ŠŽŽŽŒŒŒŽŽŽŽŽŽŽ‘ŠˆŠŒ‰‡ŠŠ‰‰ŒŽŒ‹‹ŒŒ‹ŠŠ‹ŽŽŽŽŽŒŒ‘‘’‘ŽŽ’’’‘’“‘’”’‘“”“’‘’’‘‘”•”•—–•““““’“””’Š‹ŒŠŠŠš^;% &)*+-023568<BEDGLPSW[^`a`abbca``_^\[[Z]^]]`begikkijnpmjjiigedc`\UI9*KOV[`dc^Z\`_[Z^biihfgghgeb`__[]bjnmg_^__bgkljjkkkpstwz}}~€‚ƒ‚‚‚~{{}€ƒ…‡‰‰†ƒ„…„‚…ˆˆ‰‰ˆ‡†„ƒ„†‡ˆ†††‡‡†„„‡ˆ‰ˆˆˆ†…†‡†…†‡ˆ‡„ƒ„‡‰‰ˆˆŠ‹Š‰‡‡‰‰‡‡ˆ‹ŒŒŒŒ‹ŒŽŒŒ‹ŒŽ‘‘ŽŽ‹ŠŽ‹Œ‹ˆˆ‹ŒŽŽ‹‰‰‹ŽŒ‹‹‘‘’‘’’‘‘Ž‘‘‘“’‘’”“‘‘’“‘‘Ž‘••“”––”’’““‘’’‘Ž‹ŠŠŠ‰‰‰—pY@$!&+,.1123689;?CDFINSVZ]`abbcdedcb`_][YW[_^]`dfhjkkkkmnkjiiheddd`[TH5!W\chknmjhjmmjikqvxrrrsrtnmqsrporx|zvrqnoq|{||}}wwxwxy}€ƒƒ‚€~‚„„…‡‰ˆ…ƒ…†„‚ƒ‡‰‡ˆ‹‹‰‡…„†ˆ‰ˆ…„„„…†……ˆŠŠˆ†††…††„„‡‰‰ˆ††‡ŠŒ‹‹ŠŠ‰ˆ‰ŠŠ‰‰‹ŒŒŒŒŒŽŽŽŒ‹ŒŽ‘ŽŽŽŽŒŽŽŽŠ‰Š‹ŒŽŽŠˆ‰ŒŠ‹’“•“’’‘ŽŽ’”““’‘’““““’’““‘‘’’‘‘‘‘‘’•””–—•”’“•”’’‘ŽŒŒ‹Š‰‰ˆmX?##(,.144369:::<BGHJNTWY\`cddfghgeb`_][XWY^`_aehhjlmnmmlkkkjgffgfa[SF0`dlpqrqpopsutsrswywwxxxurruwvtux|}{xwwwxy~~~}|zxxxy{€€€~~„…„„…‡…ƒ‚„…„ƒ……„†ŠŠˆ†…†‰Š‹‰†„‚‚„……‡‰ˆ†„…†††…‚…‰ŠŠ‰‡‰‹ŒŒŒŒŒ‹Š‰‰Š‹ŠŠ‹‹‹‰‰ŠŽŽŒ‹ŠŒ‹‹ŒŒŽŽŽŽŽŽŽ‹‰‰Š‹ŽŠ‰ŠŽŽ‘’‘’’‘Ž‘’’’’’‘‘’’“”“’‘‘‘ŽŽ‘’““““““““‘’–—”’’”–”’’‘ŽŽŒ‹ŠŠŠˆŽkR;!#(+.03337;988:AHKLNUYY[_ceffhigeb^][YYXY\_`behhjmopnmllmmkggijga[RB)gjprrrrstuxyxwvvx{{z{|{xuvz|zwwz~|z{||||‚€}zwx|~€‚‚‚€€~~~~~€ƒ„„ƒƒƒ‚€€‚„ƒ€‚ƒ„…‰‹ˆ†…ˆ‹Œ‹Šˆ†„‚ƒ……†ˆ‰‡…†††††…‡‰Š‰ˆˆŠŒ‹‹ŒŒŠ‰‰‹ŒŠŠŒ‹Šˆˆ‰‹Œ‹‹ŒŒŒŠŠŒŽ‹Š‰ŠŒŽŽŽŽ‘‘‘Œ‹‹Œ’’ŽŒ‘’““‘ŽŽ‘’‘‘‘Ž’”““‘’“’‘Ž’’’““”––”“‘‘”–•’’“”“’’’ŽŽŒŒŒŠ‹’dF5"&*/12127<:78;AJOQRUY[]adeegjkigda^[Z[YY]^`beggjnqqonmllljgfikg`[Q;!jlqrpoqtwwyzyxwuwz||{{zxwxz{yxxz}~{{~€€‚}}|yvy‚ƒ‚€€~}~€€€€~}}€‚‚„…‡Š‰‡†‡‰ŠŠ‰‰ˆ†ƒ‚ƒ„„ƒ†ˆˆ‡†…†‡ˆˆ‰Š‰‡‡‡‰‹ŒŠŠ‹Œ‹‰ˆˆŠŒ‹‰‰Š‰‰ˆˆˆˆˆ‰ŠŠ‹Œ‹Š‹ŽŽ‹Š‰‰‹ŒŽ‘‘‘ŒŒŽ‘’‘ŽŽ‘““’‘ŽŒ‹ŒŽŽ“•”’‘‘’’’“’‘‘’”•”’’‘‘“””’‘’’’’“’ŽŽŽŒŒ‹ŠŠ’`B1!%(-13214898:=AHNSUVY]^_abdgkkihgdb`_^\\^_`beghjmonmmmkjjiffghf`[O6kmqromptwxyyxyxvwyyzzzyxxz{zyyz{}}}ƒƒ‚‚‚€}{{{yx|ƒƒƒƒ‚€€~‚‚ƒƒ€~~~€‚‚€„‡ˆ‰Š‰‡†‡ˆˆˆ‡ˆ‰ˆ†„………„…ˆŠ‰ˆ†ˆŠŒŒŒŠˆ‡‡‰ŠŒŠ‰‰‹Œ‹Š‰ˆ‰‹ŒŠ‰ŠŠŠŠŠŠŠ‹‹ŒŠŠŒŽŽŽŽŒ‹‹Œ‘“““’’’’ŽŽ’’‘“”““”“‘ŽŒŽ’”•”’’’‘‘‘’•–““••””•”““““”“’““••••“‘‘’’’’’‘ŽŽŽŒŠ‹•_>,"'),154237:<>BDIOTWX[_``acehjlkjigedda^]^_acfhiklmmllmkjjiffggfaZN6hjnomknsvvwwvwxxxwvvwwxyyz{zyyzzz~€ƒƒ‚‚‚{yyz{{}€€‚„ƒ€„…„ƒ‚€€‚ƒ‚€„…„„„„ƒ…ˆ‰Š‰ˆ‡…††‡‡†‡‰‰†„…†‡††‡ŠŠ‰‡ˆŠŒŽŒ‰‡‡‡ˆ‰‰ˆ‰‹Œ‹‹ŠŠ‹‹‰‰ŠŠŠŒŽŽŒŠŠŒŽŒŒŽŽ‹‹ŽŽ’““““’‘‘ŽŽŽŽ’“’’“’Ž‘‘‘’“’’‘‘“””””•—˜––––•“”•••””“““’“––••”’“”“’‘‘‘‘ŽŽŽ‹Š‘Y;'$)+,044447<==BFINSVVY^_`bdfhijjihhffea^]^_acdfgjjjlllnnnmjgeeeeaYM6bfjkjjnruvvutvwxwwwvvwy{{{|{yyzzy|€‚ƒƒ‚~|zy{|}€€‚†…‚€€ƒ††„ƒ‚ƒ„„„‚ƒƒ…‡ˆ‡†††…†‡‡‡‡††………†‡††ˆˆ†……ˆŠŠ‰‰‹‹Š‰ˆˆ‰ŒŽŽ‹‰‡†‡ˆ‰Š‹ŽŒ‹ŒŒ‹Šˆ‰‹ŒŽŽŽŒŒŒ‹ŒŒ‰‰ŒŽŽŠŠŽ‘‘“’““’‘ŽŽŒŒ‘““’““‘ŽŽ‘’““‘‘“’‘“”’‘’”•––˜™™—–••”’’“”•––•”“’”—–””•”••”“’’“’’‹ˆV>" %*,.25776:>>>AFJNRRSX\_`bcehgggfgihfda_^_`acdehjjlnopprsplifcbcaZL4\agihjnqsuvutuwwwz{yxxy{|}}|{{}|z{}~‚ƒ~|{z{|‚ƒ‚€‚„„‚‚ƒ„„ƒ‚ƒ……„……†††‡ˆ†………………„ƒƒ„……„„…†…„…„ƒƒ„‡Š‹‹‰Š‹‹Šˆ†‡‹ŽŒŠˆ‡††ˆ‹ŒŒ‹‹ŒŒŒ‹‰‡‰‹ŒŽŒŠˆˆŠŒŒ‹Œ‹ˆ‡†ˆŒŽ‹‹‘‘“’ŽŽ‹Š‰‹‘‘‘’ŽŒŽ’”“‘‘‘‘‘’’‘‘‘‘“”••–˜—•“‘“”’‘’’“•–••”““””“’“•–”””””“’‘ŽŽŽP8!&(*.46888<??>AFIKMNRW[^`bcefgfedfhigdcaaa``bdfjnprqqrrrrqnjfcbb`[M0^afijloqsuvuuvvwy||zyz|}~~~~|}}|{{||}~~~}{{{}€ƒ„„ƒ„…„ƒƒƒ……„ƒ‚‚‚„…††…†‡ˆ††‡‡‡…„„ƒ„…ƒƒƒ„……ƒƒ„„‚‚‚‚‚‚…ˆŒ‹Š‹‹Š‰‡‡‰ŒŒŠŠŠˆ†‡Š‹ŠŠŠ‹‹Œ‹‰ŠŒŽŽ‹‰ˆ‰Š‹ŒŒ‹‰ˆ†‡ŠŽŒŽ’“’‘’”“ŽŽŽŽŒ‹‹Œ‘’‘““’‘’‘‘’“””““•—————–”’“••”“’’•–•••••••”“’•—•“•–”’ŽŽ’’~Q6#&(*.478:;=???BFJKNSWZ^acefghijjiijjjhfeca__aceiosttsstsrqokgcbb`\L-bdgkmqrrsuvwvuuw|~|zy{~~}|||{zzz{|}~€€€}|}}ƒƒƒƒƒ„„„„ƒ„„„„ƒ‚ƒ†ˆ‡†…†‡…ƒƒ…†‡†ƒƒ……„…‡ˆ†ƒ‚ƒƒ‚‚‚ƒ‡ŠŠ‰‰ŠŠŠŠˆ‡‡‰ŠŠŠ‹Œ‰‰‹Š‰ˆˆ‰‹ŒŽŽŒŠŠŒŽŒŒŽŽŠŠŒŒŠˆ‡‡ˆ‰ŠŒ““‘‘‘’‘Ž‘‘ŽŽŽŽ‘’’’’“’‘‘““‘‘“”•–••–——–••–•””––•”””——••••––——•”•—•’“”“‘ŒŽ“ˆrO5 $&)+/4569===>>BFHKQVZ[_bdefghilmmmlkkkhedb``abcfjnprsrrqqqnkhdcc`[L*^cinrtuuuuvwurrw}}{yz~€€}{{{{zyyz{|}~€€~€‚‚‚ƒ€€‚ƒ„………„ƒƒƒ„„‚†Š‰†……„‚‚ƒ…‡‡„‚…††‡ˆ‰†ƒƒ„„„ƒ‚„ƒƒ…‰Šˆ‡‰Š‹‹‹‰ˆ‡ˆˆˆ‰‹ŽŒ‰ˆˆˆ‰‹ŽŽŽ‹‹Ž‘‘‘‘‘’ŒŒ‹‹ŽŠ‰‰ˆ†‹’’‘‘‘‘‘‘‘‘‘’“‘ŽŽŽŽŽ‘‘’”•““”““””•”’‘’’““•———––—–•••”””–—•••–˜˜—˜—––—˜˜˜–•–•’’’’’‘ŽŽ—fK3!%(-147779=??@BFHIMRWZ\_beffgiiknpponlkifdccbccdfhjmpsrpoppnkifdc`\M+Zajquwwwxvuvurrw|~}zxx{~~~~|{||zyz|}}~~€€€ƒƒƒ‚€~ƒ„„ƒƒ„ƒƒƒƒ‚€ƒˆˆ†…„€€‚…‡‡…ƒ‚‚„…‡ˆˆˆ†…†††††……†…„†‰‰†…‡‰ŠŒŠˆ‡ˆˆ‡‡ˆ‰Š‹‹‹ŒŠˆˆ‰‹ŽŽ‘‘’““’Ž‰ˆŠ‹ŒŒŒŽŽ‹‹ŽŽŽ‘‘‘’‘‘’’““‘ŽŽŒ‘‘‘’“’“”“”•””–•“’““”••••••–•”““’’“–—•••”•—˜˜˜—––—––•“”•“’’’’‘ŽŽŽŽšzbM0#&-259:9:=?@CEILLNSWZ\^befefhijlnpqpnlifecccddfhjlnnoonmmnokhgec`\N-Zclswzyzzxvvuttx{{{xvwz|~~~~}}|{{}~€€€‚ƒƒ‚€€ƒƒ„„ƒƒ„„ƒƒ‚€„ˆˆ……†‚€ƒ…‡†…ƒƒ…ˆ‰‰ˆ‡‡‡ˆ‡‡ˆˆˆ‡…„‡ˆˆ…„†ˆŠ‹‡‡‰Šˆ‡ˆŠ‹Š‰ˆ‰‰ˆ‰‹Ž‘‘‘‘‘’““‘ŒŠŠŒŽ‘‹‹Œ‘’’’Ž‘““’‘‘’’“’‘‘‘Ž‘’‘‘‘’“••••”•—•“’“”••”“““•—–“’“““”–˜——–••——˜š›™—–••–•”••”“’‘‘Ž—i\J,"(/469<;<>?AFILMOPSVY\_cggfgjlmmnpstrnjggecddehkmnqqppqomnokgfeb_[M-[bkqtwyyzywwwxxxyzzwuuwz}~~~}|~€€€~|}~€„„ƒ€‚ƒƒ„„……„„„……„„ƒƒ„†ˆˆ††‡„‚‚ƒƒ…†…ƒ‚…ˆ‰‰ˆˆ‡‡‡‡ˆ‰‰‰ˆ†……ˆ‰ˆ…„…‡ŠŒ‹‡‡‹Œ‹‰‰‹Œˆ†‡ˆ‰Œ‘Ž’“’‘‘‘ŒŒ‘‘‘‘ŽŠˆŠŒŽ’““‘‘’’‘’““’‘’’‘‘‘“••”““””“’•••“’“”–˜–’“•–———˜———–•–•—™š™—•”•——–””••’‘’’‘‘•VUF)$+2668;;>?@CGJLMOQSVX[_begghkmoonnqtsnihhfddeegjjkoqrstrpnmkgda_]YK+Z_fkptxyyyxyzzyxyyxvtuwz~€€€€€‚‚€||~……ƒ€ƒ„ƒƒ…‡††††‡‡‡‡†††ˆ‰‡†††……†‡††‡†ƒ„ˆŠ‰ˆ‡‡‡‡††ˆŠ‰ˆ‡…„†ŠŒ‰‡†‡‡ˆŠŠ‡‡ŒŽ‹‰‹ŽŠˆˆ‰Š’Ž‹ŒŽ’’ŒŽŒ‹’’‘’““‘ŒˆŽŽŽ‘’‘‘’‘‘Ž‘’’’“”••”””““’’“””””••”’‘‘’”••””•—˜™—••–——™™˜——˜˜–––—˜˜———–•—˜–””–—•““””’Œ•“UT@'(06::;=?AABFIKNPRSWYZ\`bfijkmoqqonopqokijhffghiiiimprrssqnljgb^_]XJ)TVZ_cmruuwxy{|zxxxvuttwz}~~~€‚‚ƒƒ‚€}}~……‚€ƒ…„ƒ…††…‡‡‡‡‡†……†‡ˆ†…………‡‰‰ˆ‡‡‡…‚‚…‰Š‡†‡‡††‡‰‹‹ˆ†…„„†‹‹Š‹Šˆˆˆˆ‡ˆŒŽŽŒŠ‹ŽŽŽŽŒŒŽŒŠŒŽ‘Ž‹‹Œ‹‰‰‹’””“‘‘‘Ž‘’’’’‘‘”•••”“’’“’“””””•••“““’’“••–—˜˜—––——”“–™™——˜˜—––—˜—•—˜˜–—˜—““–—•“’“’‘‹‘SM:%#,27<>?ACCBDHJKNQSUZ]]]`cfjklmorqpomklmljihgghjkjjjlnonnonmkhea`a_XK*KKMPWalorvxy{|{zxxxwuux{}}}~€‚‚‚‚}}€‚„„‚€€‚„„ƒƒ„„…‡‡‡††…„„††††……†‡Š‹‹‰ˆˆ‡„ƒƒ†Š‹‰‡ˆˆˆˆŠŽ‹‡„„„…ˆŒ‹‹ŒŠˆˆˆˆŠŒŽŽŒŒŽŽŽŽ‘ŽŒŒŒŠˆ‰‹Œ‘““‘‘’’’‘‘‘““’‘‘‘‘ŽŽŽ‘’”•••“’’“““”––—––—–••”’‘’”•–—˜—•”—™—”“–š›š™™˜—––—˜—–—˜˜––—–”“••”’‘‘“’‘Œ‘PG7$"+37<AEEEEFFGJLLNQSV[___acgloppqsrqrpmjkljhghhgiklllmnmmmmmlkgdbab`ZM+VUQPRW_hpvyz|}}|zzzywwy|}}|}€~~€€‚}~€‚‚€‚ƒ‚€ƒ„…„…………„„…………†‡ˆ‰‹Œ‹ˆ‡ˆ‡ƒ‚ƒ†‰Šˆ‡‡ˆˆ‰ŒŽŽˆ†……‡Š‹ˆ‰ŒŠˆˆ‰Š‹ŒŽŽŽŽŒŒŽŽ‹ŒŽŒ‰ŠŠŠŒŽŽ‘ŽŽŽŽ‘‘’‘ŽŒŽ’“““““’“”””••••””——•••”’’“”•—˜–””–˜—––˜™™™™™—•••–˜————–••–••”””’’’•“‰Š†U@4#)18<?BEGHGHIJKNNNPSX[]^_abejnpppqqqsrpkikjhghgeefgiklmnmnmmmkgeccb`\N,[RQZfqwz|}}}}{{{zy{}}}}||~~|{}}}}~€~„„‚„…„ƒ…††‡‡†††……†‡ˆ‰‹‹Š‡‡ˆ‡ƒ‚ƒ„…†‡††‡‰‹ŽŠˆ†‡‰‹ŒŠ†‡Œ‹‰‰‹Œ‹Œ‘ŽŒŽŽŽŽŒ‹‹ŒŒŽŒŠŒŽŽŒ‹ŠŠŠ‰ŠŽ‘ŽŒŒŽŽ‘“‘‘’’’“””’’“’“““”•••–˜—–””•”“”˜˜––—–––•””—™˜–••––—™™™˜˜˜˜—–••–˜™™™˜—–——–––”“““’”–—‡‡M:0 $08?BCDFJMLLMORUUUUX[^^^_acfjmoppqqqrsrollkiijhfeeegkmnppommmkhedddb]M,+OW*NRXdpwz|||{{{zzzz|~}||}~~{xxy}‚}{yyy{|}ƒ††ƒ‚‚„…„„‡‡‡ˆˆ‡‡‡†…………‡ˆ‰ˆ††‡†ƒ‚ƒ‚‚ƒ„„„†‡ŠŒŽ‹‰‡‡ˆ‰Š‹‰†‡‹ŒŠ‰‰‹Œ‹ŒŽŒŒŒŽŽŒ‹ŠŠŒŽŽŒ‹ŒŒ‹‹Œ‹‹ˆŽ‘‘Š‰‹Ž‘‘ŽŽ‘““‘‘“”“””“”“’’“’––”””“”•––––—“’’““’”—–••––—˜—•“–™™˜—•””—™š™˜˜———˜˜—–—™™™˜—––———–””••••”’‘Š‡vI8-)4;@DFFHMRRRQTY[[Z[]_`__`acfhjmnorqpopqnmlkiijhfefgiklmpqommnlieddec^M*Ï¿ŸkSR^muz{{zyzzzz{|~€€~~}zwwx{€ƒ‚}zyxwz~„‡†„ƒƒ„……†ˆ‰‰‰ˆ‡‡‡‡†…„ƒ…‡‡‡‡†‡†„ƒƒƒ‚ƒƒ„…‡ˆŠŒŠ‰‰‰‰‰ˆ‰ŠŠ‡‡ˆ‰‰ˆ‰‹ŒŒ‹Š‰‰‹ŽŽŽŽŽŽŽŽ’’‘’’’ŽŒŠŠŒ’“’‘‘‘””““”•••”’’“”““”“•——•””•–—–•”‘“““””””““”–——–”–—˜š™–––—˜™™˜——™››™˜ššš™˜—••–———–•”“•–”ŽŒnR7(#-9@EHJKLQVWWVY\_^]]_abcbbcfhhijlosspnnmlklkkjkjgefhkmnoqqpnoonkhfdcb^M)ëåϤR_lty{yxy{{zyz|~€€~€€€}zxwxz~‚ƒ}|{y|‚‚„†…ƒƒƒ„……„…†‡‰ˆ†‡ˆˆ‡‡‡‡‡††‡‡†‡†„ƒ„…†‡‡††ˆŠŠŒŒ‰ŠŒŽŽŒ‰‰Š‰ˆˆˆ‰‰ˆˆ‰Š‹Š‡…†‰‘‘ŽŽŽŽ‘‘ŽŽŽŽŽ‘“””’““’‘ŽŒŒ‘’’’’’‘‘’““’“•–•’’““’’“’“••”‘’”•””“’’””“““””“’”•—˜—–••—šš™™š™——˜—––›™™š™™™˜˜•““””–—•’’“•“Ž’seO4 '1<DGHJKLPUYZZ[]^^]^acdfggghiiiiknstsolkjihjkkjjhffgilmopponoonmkheba_N)ëèÞ¿Q[ktxzywy||{zz{|}€€€~|{zyz~‚ƒƒ~€ƒ„„„……ƒƒƒ…‡†„ƒ„†ˆˆ†‡ˆˆ‰ŠŠŠˆ††‡‡††‡……†ˆ‹Œ‹ˆˆ‰Š‹ŠŒŽ‘ŒŒ‹Š‹ŒŠˆˆŠ‹ŒŒˆ…†‰‘ŽŽŽŽ‘‘‘ŽŒŒ‘“””•”••”‘ŽŒ‘’““’‘”•’‘”––““““‘‘’’“””’’””“’“””‘’”••••–—–•–—˜˜™™—–•—››ššš™˜——˜˜—›ž›››š˜™›šš—”’‘’–˜–“’’“’—dZI/%1<DHJIKLNSWZ]^^__``begghjjjjjkjjknrttqmmlhegjjiijkllllmoooonnnmljifdc_O*âãÞÅ29[luwxxwy|{{{{zz|‚‚‚}||{{|‚ƒƒ‚ƒƒ…‡†…„„‚‚…†…‚ƒ…‡‡‡‡ˆ‰ŠŠŠ‰†„„†‡‡‡ˆ‡‡‰Š‹Šˆ‰ŠŠŒŒ‹Œ‘‘Œ‹Š‹ŽŽŠˆŒŽŽ‰‰‰‹ŽŽŽŽŽ‘ŽŒŒŒŒŽ’’’‘’”••“‹‹’“‘’••‘’••““•–Ž‘’”–•“•–•”•——•”””••–—–”•—˜˜˜™š˜——˜š›š™——˜˜™››š›œ›ššš™™™šššœ—“‘’”–”““““‘‘¢ŒLSA+".;EIIJIKNQUYZ\^__adegjjihiihiklkkkmpsspoonjgfhihghloppoopponnnmkjjjihfaO,âãäÙ‰JGjuxwvvz}}|}|{{}„„‚€}|}}|~€‚‚‚€ƒ…†‡ˆ‡„€€‚„…ƒ€‚„…†‡‡‡ˆ‰‰Š‡„ƒ…ˆŠ‰ˆˆˆŠ‹ŒŒŒ‹‹‹ŠŠ‹ŒŽ‘ŽŒ‹‹ŠŠŒŽŽŠŠ“’ŽŒ‹‰ŠŽŽŽŽ‘‘ŽŒ‹ŽŽŽ‘’’‘’”“‘ŽŒŒŽ‘’’‘’““’Ž‘“””“’‘””’“•–”‘‘•˜——™š˜———˜–”•–––—˜—––—–•—™šš™˜™š›š™——™šœžœœœ›šš››œ›œ›››š•””••”••”“‘’šƒ@H;'(4?HLMLLNQTX\\]`cdefhjlljjjihkoponnoprtsqpomjhhiihilpqpoqrrqppomllllkjhbQ-äåêæÁˆ"crwwvx|~}{|}}~ƒƒ€~}{{{|||€€‚†‡ˆˆ†‚~€‚„„‚‚ƒ„†‡††‡‡‡„‚ƒ†‰‹‹ˆ‡†ˆ‹ŒŒ‹Š‹‹ŠŠŠŠŒŒŒŒŒ‰ˆ‡ˆ‰ŠŠ‹‹Œ‘ŽŽŒŠˆŠŽŽŽŒ‹ŽŽŽ‘‘”•”““’’‘’““”•“““’’””•“’‘’•——˜™™———™˜––—˜—˜š›š˜—–••—™›š™˜˜™™˜———™›œ››œœ™˜™š›››œœ››š—–––——–•“‘’™’vH?4#,6>GMOMNRTVY^^_adeeehkllkkkjkmooonnoqruusrqnlkkkjiilnonmnprrrqommnnmkigdS,ææëéÓ¤]muvwz}~{yz}€€€€}{zz{|}~€€€‚€…‡‡‡…€‚ƒ„…‡†……„……††††………}~‚†ˆ‰‰ˆ††‡ŠŒŠ‰‰‰‰‰‰Š‹Šˆ†…†……†‰‹‹ŒŒŽŽŽŽ‹‰Œ’ŽŒŒŒŽ‘‘Ž‹‹‘‘‘‘“•“’”——•”“‘‘””“”–”“”“”“’‘‘’’“””•–˜™™—–—š™˜™š˜—™œšš™˜˜˜—š›››š™™™™—–—˜™›œ›™™œš™š››››œ›šš›™˜——™™—”’™š€fD7*$3<BIPQPQUXY\abacfgeeijklllmnooppppppqqstttspmllkihjmopnllmpqrqpopqoligfdU,èèëêÔ—Zluwxy|}{yz}€~{zz||}€€€€„†‡‡…„†ˆ‡†„…ˆ‰ˆ†…………†††…„„‚………†ˆˆ‡†ˆŠ‹Šˆ‰‰ˆˆˆ‰Šˆ…‚ƒ…†‡‡ˆ‰ŠŠŒ‹Š‰‰ŠŠ‹ŒŽ‹‹““ŽŠ‰‡‰‹Œ‘’‘‘‹Œ’““‘““’’•——•“‘Ž‘“““”•’ŽŽ‘““““’’””‘’”˜———––——˜šš–•––•–˜™™™›œ››š™˜˜˜˜—–—˜™››š˜™››ššœ››ššš™˜˜™˜–•—˜˜–“‘–ZVE2!(7AEJQSRTWYY\`aacgigfiijlllmnpoopqrqqqppqstrnmkjhggimoomllkmopqrrttqlgeedU*èèëëÑ’ ]owxwwz|{zz}~}~~|zz{}}~€‚ƒ‚€~~‚„……„…‰Š‰‡„„†‰ˆ†„„………†‡‡…„‚‚‚„…„ƒ…ˆ‰‡…†ˆ‰‰‰‰Š‰‰ˆˆ‰‡†……ˆ‰ŠŠ‰‰‹ŒŽŠ‰‰‰‰‰Œ‘‹‹Ž‘‘‰ˆˆˆ‰ŠŒ‘’’’‘Ž“”“’‘‘‘‘’’•—–•”’‘‘’’’“”““””’‘’”•••”“’‘••‘“•–––––””—››˜••–••—™š›œœ›™˜˜˜™š™˜™šš››š™™››œ›››š™š™———˜—––——–””’Ž’…LO>+ -;CHLQRSUXYZ\`bcehhgikjkmmmnpqpppqqqpppoprrnkkihhgfgilkkmnmmmnprtvvsnhecbS(èèëëÄ^)bsxxvuxzzy{~}~€~{{}~~~„…ƒ~}~€€‚ƒ„„ƒ…ˆˆ†„‚€„…„ƒ‚ƒ„…‡ˆ‡†…„…††††…†ˆˆ††‡‡ˆˆˆ‰ŠŠ‰ˆˆ‰ˆˆˆ‰‰Š‹‹ˆ‰ŒŽ‹‹ŒŒ‹ŠŽ‘“‹Š‹ŒŒ‹‹‹ŽŽ“’ŽŽ’‘ŽŽ‘’”•”•”“”–•””””“““’‘‘’“••–––•’’––“’’’“““••”’”›š™—–—˜–•—™›œš™˜——˜™šš™™™™ššš™™šœ›˜˜™™š˜–—˜—–•––•“““”“€KH9&&2<CHMPRUVWXZ^acdfghiklllnnmnopppppnmnppopqqnkjihiigeefgknoonnnnprtuspkfcaR(çèëëLjuwvuuwyyy{~€€}|}~~€ƒ†…ƒ‚€€‚ƒƒƒƒ‚ƒ……„‚€~ƒƒ‚‚„†‰‰†……‡ˆ‰‰‰‰‰‡††…‡ˆˆ‡‡ˆ‰Š‹Šˆ‡ˆ‡ˆŠ‹ŒŒŒŒ‰‰ŒŽŒŒ‘‘‘’’‘ŽŽŽŒ“”“‘‘‘‘ŽŽŒ‹Ž‘’””••““•—–”–—–”’’‘‘’“”–—––——–”“•–—–“’‘‘‘’”“’“—™™™˜˜š™—˜›žŸœ—˜˜——™š››ššš™™™ššš››˜™ššš˜—˜˜—•””””’‘‘—–Œ|UC3"#.8BGJNSUXYY[]_bdefiijjmnnprqpppppponmoppqrsspmljhijihhiknpooooopqrttrokgc`R(éêëê[dovwvvwyzz{|~~€‚‚‚}~€ƒ„„ƒƒ‚‚‚ƒ„„„ƒ€€‚ƒ‚~€ƒ…ˆŠŠˆ……ˆŠŠ‹‹ŒŒ‰†…†‰Š‰‡ˆ‰‰Š‹Š‰‡ˆ‡‡‰‹ŒŒŒ‹‰‰ŒŽŽŒŒŒŒŒŽŽ’“‘ŽŽŽŽŽ‘“”“’‘’”••“ŽŒ‹Ž’””•••’’”•””–—•“‘‘’’’’’”–••—™™›”’•——•“““‘‘‘‘‘“”˜™™˜˜™›™™œ œ˜˜™™—™šœ›šš››™˜šœžœš˜™››šš˜˜™˜——––•’Ž’—fOA0 *6@GIKOSUXXZ[]_`acehihhknnoqqpoppooppppoprsrqonmkihiiijklopnnoopqrsttrnjfc_P'èçêä]dovxyz{|{z{}~€‚ƒƒ‚}}€‚‚‚‚‚€ƒƒƒ„ƒ‚‚€‚€‚‚ƒ…†‡ˆ‰ˆ†„…‰ŠŠŠŠ‹‹ˆ††ˆŠŠ‰ˆ‰‰‰Š‹ŒŠ‰‰Š‹ŒŽŽŽ‹Š‹Ž‹‹ŒŽŽŽ‹‰ˆŽŽŽ‘’‘‘“”“’‘“•–•“‘ŽŽ“••••–”“””“”•–•“‘‘““““’”––•–š›™”’”——••••“’’“”–˜™šš™˜™œœ›ŸŸšš›œ›œŸžœœš˜šœžŸŸ›šš››››™˜™™™››™˜“‘Ž”›u[P>)!-9CJLMPSSVXY[]^_`cegjjjlnooqqqqrsqqrrrqoqssqqononmjihhikmopqqsrqqqqrqnkheb^M$½ºÒÃZeqxzz{}}z{}€‚‚ƒ„‚~{{~‚‚‚‚‚€„„„„ƒ‚ƒ„ƒ‚„…„„……„„†‰Š‰ˆ‡„ƒ„…‰Šˆ‡ˆŠ‰‡†‡‰‰ˆ‡ˆˆˆŠŒŽŽŒŠ‹ŒŽŽŽŽŒ‹ŒŽŽŒŠ‰‹ŒŒŒ‹‰ˆ‰‹ŒŒ‘’““‘‘’’’‘‘“””’‘‘’’‘‘”••”””•”“””“”””””’’”””“’”••“’ŽŽ‘”––••––•”——˜™™šš™™˜˜›œžžœ›šš›œš™šžžž›™š›œž Ÿ›››šššš™˜˜˜š™–””“‘“cSK8!'/:DJMOQRRUXYZ]_`adfhjllnpqpqqqptusrsstqpqrrrrqoppomjgfhjmpqrrttssrpolihgeb]J#•¤º”Rcsz{{|~}{|ƒ……„…„€{yy}ƒƒƒƒƒ‚‚„‡‡††„‚ƒ…ˆ‡…ƒ„‡‡††ˆ‰‡‡‰‹‹‰‡†ƒƒ„†ŠŠ‡†‡‰‰‡‡‡‰ˆ†‡‡ˆ‰‹ŽŽŒŒŽ‘‘Œ‹ŒŽŽŒŠŠ‹‹Š‰Š‹ŒŒŒŒŽ‘“”‘‘’’‘‘’””“Ž’“’‘‘”•”””•–—–••–••”“””“’’”•••––”’‘‹‰Ž’””••••—˜™ššššš›š˜™š™šœžžžœ›š™›œ™––—›œ››šœ Ÿœœ››››››š™—˜˜—““”“ŽƒWTE.#1<CHMOPSTSUX\^_accefikmnoqrqqrrruvustttrqqrrssrppqrpmjijknqqqqrtuusqolhgfeb^I"ÐÄÔÃSeu{||{|}{y{ƒ…†††„{zz}‚„ƒƒ„ƒƒ„†ˆ‰‰‡…„„‡ˆˆ†ƒ„‡‡‡‡‡‡††‰‹Š‡†‡†……‡Š‹ˆ‡‰‹‹‰‰‰‰‡‡ˆˆ‰ŠŒŽŽŒŒŒŒŽŽŽŽŒ‰ŠŽŽŒ‹‹ŒŒŒŒ‘‘ŒŽŽ‘”’’“’Ž’““’Ž‘“‘’”“‘’“•—˜˜—•””•”“”•“‘’”–––”’‘‘Žˆ’’’“””•—šš›ššš˜™™™šš™——›œ›™˜™™š›™˜–•“™žžœšš›žœ›››ššššš˜——˜š™˜–““›’|ZL<&&7DHJMNPSUTUW[^``bcefhkmnnooopqqqsuttttsrqqrsssrpqqppnlkjjlmmnoprssrppokigdb^I"èåèÜQeu{|}|zzzyz~‚‚ƒ„ƒ}||~ƒƒƒ‚ƒƒ„†‡‰Š‰†……†‡†…„ƒ„†††„„„…‰Œ‹‡†‡‡†‡ˆŠ‹‰ˆŠŒŒ‹‰ˆ‡‡‰‹‹‹ŒŽŽŒ‹Œ‘Ž‹Š‹ŒŽŽŽ‘’“ŒŽŽ“’‘‘‘Ž‹‹‹‘‘‘“”“’””“‘’“•–—™™•’‘“”““”“‘‘’”–––•“’““˜›˜”““••””–™›œœ™™œžš–““œ™™˜™››š›››˜–—›žŸžž›™š›œœ››š™™››˜—˜™›œ›˜””¤‹tXE0,<FKNPOPSUVWXZ]_`acffhkmmnnnorsrqrstuutsrrrstsrrrqonmmlkjjkjjlmmnopnnpomkhd`[G"æçéÚOas{|}|zyyz{}}}}}€~~~€ƒƒ‚€ƒ„…†‡ˆˆ†ƒƒ„…„„ƒ‚ƒ……„‚‚„†‰ŒŠ‡†‡‡ˆˆˆŠ‹‰‰‹ŒŒ‰‡†…ˆŒ‹‹‹ŒŒŒŽŠ‰ŒŽŽŽŽŽ’“’‘’‘‘ŽŒŒŽŽ’’ŽŽŽ‹‰‰ŠŒ“””””””•“’’“””“”––’’”“‘’““””•–••••””–š›š”“˜—–”“–™›œœœ›™šŸ™“”—™™šžžœœžœ™™šœ™š›œ›™˜š››š›š˜—™š˜˜˜™™™™–“Ÿ¢zbR<%0=AFKNNOQTUXYY[]^`bddegjkmmmorsrrrstuuttsrrstrpprqnkjjiijklllmnmmnnnmnnmlje^WD æåèØQau{}~~|z{|}|{{{{|~€€‚‚„„‚‚„……††††„ƒ„„„ƒ‚„††„ƒ…†ˆŠ‹Š‡†‡ˆ‰‰ˆŠ‹‰‰‹‹Š‡†‡‡‰ŒŽŽŒŒŒ‹‹ŒŒŒŽŒŒ’‘“ŽŽ‘’”“’’’‘Œ‹‘‘’‘Ž‹Š‹ŒŒ‰ŒŽ•——–––––“‘“”•••––’‘‘““’’”–——––•”–˜——™››™–•˜™˜–•–šœœ›™™™šœœ™—”••—š›œŸŸœœœ›šššš˜™ššœœœœœœœœœš˜›œššš™˜—–•‘››tXM5$2=@CHLNPSUVXYYZ\]_abbcfilnnnortttuuutttuttsssrppqpnlkighjmnoppooooponnnnmje]UAéèçÑM^sz|~}|~~|{{{|{|}}‚‚‚…†…………„…„„‚€~€‚„„ƒƒ…‡ˆ‡††‡ˆŠ‹Šˆ††‡‰Š‰‰‹‹‰ˆˆˆ‡……†ˆŠ‹ŽŽŒŒŒŒŠ‹ŽŽŽŽŽŽ‘“‘Ž‘ŽŽ‘’‘ŒŠŒ’“’’’Œ‹“’’–•”–—•••–••’‘““•———–“’““’““”•—˜˜—–“’•———˜˜™˜–•—™˜––—˜œ›˜—™šš››™™››™—–šœœœš™›œšš››š™™š™™šœœœ›™ššššš™—˜—–’‹ŽpVG1)2<BEHKMPSUVVXYY[\^_abdfiloonorrsttuutttttssrqqppqomnmjggikmprqprssrqponlkie^S=éèáÇL^py|}€€~{{|}}}~~}~‚ƒƒ‚…††‡†…„„‚€~~€‚„„„…‡ˆˆˆ‡‡‰‰Š‹‹‰ˆ‰‹ŒŒ‹‹ŒŒ‰ˆ‡‡††……ˆŠ‹ŒŽ‹ŒŽŽ‘ŽŽ’‘ŽŽŒ’••””“’Ž‘’”——–––”“””””’‘“••–˜™˜–”””••••–––˜™™™•”•—————™š˜—™œœ˜˜˜™œš˜˜›œœšš›œ›™˜™››ž™˜œž›˜œ›››žœ›žžžžžœ››››››™™š™—˜™œ „bTB-!.7?GLMNPRTWVTVZZ[]_`cfhiknqqqrsssstvwvututtssrqpqqonoolgfgikpssstttsrrqnjhhe_Q8ëëÐJZmw{|~€€}{{|}~€~~ƒ„ƒƒ…†……„ƒƒ‚€~€‚„………††ˆ‰ˆ‰ŠŠŠ‰‰‰‰‹Œ‹ŠŠŒ‹‰‡‡‡‡‡‡‡‰Š‹ŒŒŒŽŠˆŠŽ‘‘‹ŠŠŠ‹‹‹ŒŒŽ‘‘Ž’””“’‘‘•–”““”“’‘‘‘’’“”––—˜™—–•••––––—–••—˜™–”•––——˜š›™˜šžœšš™šš™˜šžž›šš››šššš™˜—šž žœžŸžœœžžžžžŸœ˜˜šœœ˜—˜˜——˜¡žŠxVN<'!/:BHLNQRRTVVUW[\]`aabfiknppqqrrrsstvwvsstuutsrqpoonmopnjhgginqsttsssttsnigfe^M1ëê³O2HYmyyz|~}||{}€€€‚…††‡‡„„ƒ‚‚~„…„„†ˆ‡……†‰ŠŠŠ‹‹‰ˆˆˆ‰ŠŠ‰‰ŠŒŠ‰‰ˆˆ‰‰ˆ‰‹ŒŒŒŠ‰Š‰‡†‰‘’’‹‰Š‹‹ŒŒŒŒŒ‹ŒŽŽŽŽ‘’’‘’“’‘Ž’”•’‘““’‘‘“””•–—˜˜––————–––—––••–˜—––––˜™™š›šššžœ››™™šš˜˜›œžž™˜˜šœœœš˜——›žž›œŸ ŸžžžžžžŸœ™™šœž™—˜˜–•––jXF1 "0:AHLPSSSUWWWY]^_cdbcgjmpqpprtssstuvvtrsuvvuspnmnnmkmoqpmjikmprstssuvvtojfdd\G)ܾC&@DShrtvy{~€{zzxy{}~}}~€ƒ…†‡…‚ƒ‚‚‚‚ƒ„††…„„††…†‰‹‹‹ŠŠŠ‰ˆ‡‡†††„‚ˆŠŠ‰ˆ‰ŠŠŠŠˆ‰‹ŒŒ‹Šˆ‡…„…‡ŠŒŽŽŒŒŽŽŒŒŒŒ’‘ŽŒ‘‘‘‘’’‘ŽŒŒŽ‘”••––’’““’‘’’“”””•–——•”•–——–””•––––”•–––••˜š™˜š›™˜šš™š™——˜˜—˜š›š›ž˜––™››››™—œž›˜šœœ››šššš›œœ››šššš››œœš™™˜–””›•vcW>&#/7?GKNPQSVWVVY\]^ab`aehknnnnqttsstuutsrtutsrpmjjlmkjknrrommnnmmprstvxwtpjecbZ@"xnI0A4:M[^ikkquxwutrtw|{z{~€€‚……ƒ‚„………„„……ƒ‚„…†‰‹Œ‹‹‹‹‹Š‰‰‰ˆ†„€€„ˆŠŠ‰‰ŠŠŠŠˆ‡‰‹Œ‹Š‰†„†ˆ‰‰ŠŠŠ‰ŒŒŒŽ‹Š‹ŽŽŽ‘””‘Ž’’“””“ŽŒŽ‘•˜˜———”‘’“’’“””””“’’•——•“’“–™—”“•–˜™™—–˜™™—•—™—–˜š™—˜™™™™˜———˜š››™™›—˜š›š›œ›œŸŸ›š›››œ›™™™šœœ›š›››››››™–•“œ–pdS7$1;BIMNOQUWXWUX\\]_aacfhkmmnoquuttuvvussttqoppmkkkjijlnqpooppnkjmqrux{xuqkfcaW90,#(3CNOQRU]`^`cflqwxux~€€‚‚€||‚…‡‡…‚‚„…ƒ‚ƒ…ˆŠŒŒŠŠŒ‹‰ŠŠ‰…‚€„‰‹‹ŠŠŠˆ‡ˆ‡†‡‰‹ŠŠŠ‡†ˆˆ‰‰‰‰ˆˆˆ‹ŒŽŽŒŒ‹Š‹Ž“••”‘’’’”••”’Ž“”–——––––•””“’‘’”•––•”‘’•–•““”–——•”•—˜™›™˜™›™–”•˜—•—˜—–—™˜——˜˜—˜š›š™˜™š››œœœœœœŸŸžžžœ››œžŸœœœžžœš™š›œ›™——”¡’f^O5&3=DIKMPQTUVVVX[\]^`cefhjllnnpqrstttutqrsromnnmklljjklnonnoonkijmprsvwxvslgcaU1"'058:=ADFKQWY]ajnu}€€~{|€ƒ†ˆˆ…ƒ……„„†‰‹ŒŒ‰ˆŠ‹‰‰Š‰†„„‚‡ŠŒŒŒ‹‡…………‡ˆ‰‰‰‰††ˆˆ‰‹Œ‹ŠŠŒŽŽŒŠŒŒŒŒŽ“””“Ž‘‘‘’“””””“’’“”•––”•––˜˜—•”““”•–——–’‘”•”•—˜–””•••—˜™š˜˜šš—”’“•–––•••–š›—–˜š™™™™™™™•˜˜šœžœžŸ ŸžžžŸžžžžžŸŸžŸ žžžœš˜—˜š›šš˜–––…VYJ1 )5?FIKOSSTTTVY\]]^_afiiijlnooppqtvtstsqpqpljklllmnmlmmnonnnmkjjlnqrrrsvwtngdaS+ #%'+08?EHJLYdn|‚‚ƒƒ~~‚„†ˆ†„‚ƒ†‡†…†‡‰ŠŠˆ‡ˆŠŒ‹‰‡ˆˆ‡‡‰ŠŠ‹‹‹‹ŒŒˆ‡‡ˆ‰ŠŠ‰‰‰‡†‡‰Š‰‹ŒŽŽŒ‰‰‹ŽŽŽ‘’Š‹ŽŽŽŽ‘“””“’’““““””””•—˜—–••••–•””•’‘”–”•˜™•“””•–––•—šš—”““”•——•”•™œ›˜—™›š™˜•™žŸž›š›œ›™›žŸžžžžžžŸžœžœœœ››š™˜––˜™šš˜–“ŠvZPE."+4<CHLPSTVVUVZ]^]]^afhhijlnoopqsttrstsqppmiiiklmmonnnmmnnoomkkkloqssqrtutngdaP&"(,/145JYh{~ƒ„……„‚‚ƒ„†‡†ƒƒƒ…‡ˆˆ†…‡ˆ‰Šˆ‡‡ˆ‹Œ‰‡ˆ‰ˆ‰‹ŒŒŒŒŠ‰ŠŒ‹ŠŽŽŽŽˆŠ’‹Ž‘‘‘‘‘ŠŠ‹ŽŽŽ‘Œ‰ˆˆŠŽŽŒŒ’’’‘‘’””’’”••”•——––——˜˜—–””•”“•–••˜˜”‘‘““””•••–˜™›™—––––˜˜–•—™››™™šœœ›š›¢¢¢¡ žœ›œš˜™œœœžž ŸžžžžŸ Ÿžžœ›››š™˜˜˜™˜—–•„o[H>*&08>DIMORTVWXXZ\^^^_bfhijkmnoprttsssuvuttrolllklmnopponlllnponnmlnquvtssttoifaL# !'4Pau}ƒ………†††††…†‡…ƒ„…†‡ˆ‡„ƒƒ„†ˆ‡††‡ˆ‰ˆ‡‡ˆˆˆ‰‹ŒŒŒ‰†‡‹Ž‘Ž‘‘‘’“‘‹Œ‘“Ž’‘Œ‹Š‰‰ŠŒŽŽŒŒŽ‘’’•”“––•–—–––––––———˜˜–•””•–——”“”––————˜™™™›š—–—˜˜˜˜˜˜˜ššš™šœžœšš¡¡¡Ÿžœ››™—š›œžžž žžŸŸžžžžžŸ Ÿžœ›š™™™˜—œ›hWE8$&1:@EJLNQUWXXYYZ]^^_adhijkllnpstsssstvvuvusqqplklmmoqrqnllnoppqpnnquwusrttpje^E*C\t}††…††††‡‡†………„…………††‚‚„…………†‡‡‡‡ˆˆˆˆˆ‰‹‹‹‡„†‘‘‘’‘‘Ž‘‘‘“•”Ž’”“’ŽŒŽ’“Œ‹ŒŽ‹‹ŽŽŽ’’‘‘’‘•—–•–——–•••••–—˜šš™—•”–˜˜–“”—š™™˜™š›š™™™—••–˜™˜˜™šš™™™šœžžœšœŸ ŸžŸžœ››™™šœœššžœ›œŸžž ¡ žŸ œ ¡ žžŸŸžœš™™™ œv`UC3&1<BFKNOSWYYZ[Z[^___bfijjkllpsvvuvwtsvwwwwwuspmlnnmnqssqoopqqrrrrqrtutssttqkeY;"<Zxƒˆˆ‡††…………„‚‚„……„„…††ƒ€€‚‚‚ƒ…†‡‡ˆˆˆ‰‰ˆ‰Š‰‰†ƒ„‹ŽŽŽ‘’‘‹ŒŽŽ‹‹’”••‘’’‘Œ‹Œ’“–“ŒŒŽŽ““’““’’‘—–”“•••”“”••”•–˜™™˜—•”—š™–”•˜š››™™œœš™™˜––––—˜˜™š››š˜™››œœ››Ÿ Ÿœœœœœššœ››ž¡¡ žŸ œ™˜žŸœžžžžŸŸ›š™™Ÿ”_XQC.&.9BFKOPTXY[\\[[]___beghikmmquwwwyyvuwwvvwxwtqnoqqonnprrrrssssstttssttttsspldS2;Wyƒ„‡†…„ƒƒ„„‚‚……ƒƒ†ˆ‡„‚‚‚€€‚„‡‡‡ˆ‰ŠŠŠŠŠŠ‰ˆ†ƒ„ˆ‹Œ‹ŒŽŽ’’‹ŒŽ‰‰Ž“••”“’‘‘ŽŒ‹ŒŽ’“”––ŽŽŽŽ‘’“•“’’’‘”–•‘’“”–˜—••™˜™™˜—–•˜›š—–—™š›œ›œœ˜—˜˜——™˜˜™šš™™š›™š›žž Ÿ››››œžžžŸŸœ›››œœœžŸ žžŸ œ™›œ›œœœœž™™š˜¡“P[O@% )/8AGLQSUWXZ]]\[]_abdfghknqpswyxyz|zxxwvvwxvsqpsuvurppqqstuuuuvvwwvutuvusrpmdM)7V|…„†…ƒƒƒƒ‚}}€ƒ„ƒ‚…ˆ‰†„„„„„…„„…‡†…‡ŠŠ‹‹ŒŒŒŠ‰‡……ˆ‹Ž‹ŒŒŠ‹ŽŽ‘’ŒŒ‘“•”’’‘ŽŽ‘’”––“‘‘‘‘Ž‘’”””““““•–”‘‹‰Š“•—˜˜”•™˜˜˜————™š››œ™šš›œš–•—–•—›š™š››™˜š››š›žŸœžžž››œ›œžžŸœœœœžœŸŸžžžžŸ ›žœš™ššššš™—˜š›¢UXN;#+09AGLPSVWWY[[[[\]`begghlpqqtwxwxy{{yxxwuuvtpnosvwxvsqooqrtuvuvwwxxwuuvvusqndK&3U}ˆƒ…ƒ€€€‚„‚~~ƒ…„‚„ˆŠˆ‡‡‡‰Š‰ˆ†…††‡ŠŒŒŒŒŽ‹‰ˆ††ˆŽŒŒŒŠ‰ŒŽ‘’“•Ž’“’‘‘‘‘‘’“’’“•–•““”•“’“•••••–••”””ŒŽ”————”•——––––˜™ššš› ¡š˜™›™—–––•˜›™˜šœ›š›œ›››œœœœš›žžžŸ ŸžœœŸ¢ŸžŸŸŸŸžžŸ¢Ÿœ›žžš™™™™™š™——––”}WUI6$+2<EIMQUXYXXYZZZ[]_cfhjkmoqruwwxyyzzyyzxuturpnoqtuwvtqppqqrtuuvvwxyxvvwxwtrodL'0Tyƒƒ‚€~~€ƒ‚€ƒˆˆ‡„„‰‹Š‰ˆ‰‹Œ‹‰‡††ˆ‹ŽŽ‹ŠŠŽ‹ˆ‡††‰‘‘‹‹ŽŽŽ‘“””’‘Ž‘’““”•”“‘‘’‘‘’’’’”–•‘‘’”–—–•—˜—•”–˜›—–•–˜––•••—–”””•˜š›š™šœœš˜–—›œš™šš™™™™š™—™ Ÿžžœ›››››ššš››š˜Ÿ Ÿž žžŸŸŸ¡£žœžŸŸŸžž ¤¥ž›œ Ÿ›ššššš›š™——•‹{_PE2$*1<FKMQVYZZXWXXY[]adfhiklmpruuvxzyxxyzzxvuurqomopqsttsrrrrsttssstwxwwwwxwurndM)0Uu€€‚~}‚‚„ˆŠ‰‡†‰‹Š‰ˆ‰Œ‹‰ˆ‡†ŠŒŽŽ‹‰ˆŠŒŒ‰†…†ˆŽŽŒŠ‰Œ’”’‘ŽŽŽ’“•–•”“““””‘ŽŽŽ’”–•’‘’“•—–”–˜—•–˜˜˜˜™™™˜———–—˜—•”””–™šš›œœš˜—˜™™˜˜šššššš™˜–— ¡ŸŸŸ›š›œ›™™šœœž¡¡Ÿž ŸŸŸ ŸžŸ ¢žœœž žž¡£§£œœŸŸœœ›œœ›™˜–Ÿ›„t]J@-$+3?IMOSWY[ZYXXYZ\_dggghjlmpsutvxzywwyzxvutsqqomopqrtvvuttuuuspppquwxywwwwusncL).Nj{€€€€~~€ƒ„ƒ‚ƒ†ˆ‰ˆˆŠ‹Š‰ŠŠŒ‹Š‰ŠŠ‹Œ‡‡‡ˆ‹‹Š‡…†ˆ‹ŒŒŠŠŽ‘Œ‹‹ŽŽŽŽŽŽŽŽŒŒ“”•”“””””•“ŒŒ‘”•”’’“’’”””––••—™˜–•˜š™˜˜š™˜˜˜˜—–••”•–˜›œœš˜—˜˜––——˜š››™—–—šŸžœŸŸ›šœ›™šœž ¡ žœžŸŸŸŸŸžœœ›œŸ ¢¡žž ¡¡ ŸŸŸžœœœ›—––¤–o`WH<'$+5@HLQVWXWXZ[[[\^adggfgijlopqrsvwvvvxxussrqpppprsstuwyywvxxwtpnnosvxyxvwvusmaI(*@`sz€‚‚€ƒ†„ƒƒ…ˆ‰‰ŠŠŠ‰‰‹Œ‹Š‰‰‹ŽŽ†…†‡ˆ‰‡‡†‡ŒŒ‹‹Œ‹‹ŽŒ‰‰ŒŽŽŽŒŠŒ‘‘‘Ž“”““’“”““””‘ŽŒŒ–—–““““‘’“““““”—™˜——˜™˜˜™››™™™˜™˜˜—•”•–˜›œœœ›™™šš——––˜šœœœš™˜˜˜•šŸŸ ŸžŸŸœŸ¡¡Ÿ››ŸžŸžžžœ›š› ¢¢ŸžžžžžžŸŸŸŸŸž›šš™—”’‘f]TI:#!&.8AGMTYYWVVZ]]]_abdfhhikkklmnpsuuvwxxwtrqqqrsuvvvvwwx{{yxxxywtpmnquwxwvvvvsnaH(%% $/M]dvƒƒ€ƒ†…ƒ„†ˆˆ‰Š‰ˆˆ‹‹‰ˆ‰Š‹ŒŒŒŒŽ‹††‡ˆ‰‡…†‰Œ‘ŽŒŠ‹‹‹‘‰ˆŒŠ‡‰’””“‘’””’’’‘‘‘‘““’‘ŽŽ‘•˜–”””’‘‘““‘‘‘“˜˜˜˜˜˜˜———™š™™™™˜——™˜––––•™šš™™šŸž™™˜——™›œ›™˜˜˜›žžœ¢ žžžžž ¡ žœžžŸŸŸŸžœœžžœŸ¡¡ žžžžžŸŸžžžš—˜™™—“˜Šc_SF5")19@FNSXXWVXZ[\]_aabcegkmlklmmpstuvwwwutsrqrtvwxwwwxxxyywvwxyywsnlnpqstutttrodG)*55.")=N_t‚‚€~‚……„…‡ˆˆ‰‰††‡ŠŠ‡ˆŠ‹ŒŒ‹Š‹ŒŽŒŠŠ‹‹‹ˆ…‡ŽŽŒŠŠŠŒ‘‰ˆ‹ŽŽ‹‡‰Ž’“””“’‘“”“‘’’“”“’’’“••””’‘‘““’‘‘“•––––˜˜˜–•–——˜˜˜—–—™™˜˜—––—˜™™›Ÿ¢Ÿ™——™›žœššœœœœžžžžžœž ŸŸŸ¢ŸžžŸ žœŸ ŸŸŸžŸ Ÿžžžž››žŸœšš›žœ–’Y\PA-$-6<BJRUVWWXZ[[\]_``bcdhklllopqsuvvvvwwvuutstuvxyxvtttvvuuuvxyxwtnllllotvutssqgM,4BB:+0A^t€~~ƒ…„…†‡‡‡‡†ƒ„‡Š‹‰†‡ŠŒŒŒ‹ŠŠ‹ŽŽŒŒŽŒ‰‡‰Ž‘‘Œ‹Œ‹ŒŒŠŠ‹‹‹Šˆ‰‘’“““’’”“Ž‘‘’’“••””“’“”“’‘’””””““”•••”•——•““”–———–––—˜˜˜——˜™™™™›¡ Ÿžš˜—™œ››œœ›œœšš›œ›œž ŸŸ¡¤¢ŸŸ¡¡ŸŸŸŸ žŸŸžžžžžžžžžœ™™œžœœœž™€VWM=%%.7=CLSVVWXY[[[\\^``acegihilpssuwwvvuvvttuuuuutvxwtqpqtuuvvvwxxwtpnljjouxwuttsiQ0!<LME3#&8\u~~~€ƒ………†…„…†…„…‡Š‹ˆ……‰ŒŒŒ‹‹‰ŠŒŒ‹ŒŽŽŒŠ‹‹’“ŽŽŽŒŒŒ‰†‡ˆˆ‰’““’““’‘Ž’“““”””––“’“”’’““”—•““•••”–——•”–—–•••–—˜™™™™˜——––›œššš žœ›š™šœžž›œžœœœœ›šš›žžœœžŸ Ÿž¡£¤¢ ŸžžžŸ ŸŸ žžžžŸŸžœš—œž›šœœ››¢‡„aSK:#/9>DKRUUX[\]]\\]_bbcdfhihjmqstvwwwvvvvtsuwvvtstwwusqruuvxyyxxxwuspljknsxyxvvsiU5&BPRL9%1[u…}|}„„„……ƒ€ƒ‡ˆˆˆ‰‹‹ˆ††ˆ‹‹Œ‹‹‰‰Š‰ˆˆ‹‹Œ‘‘‘’”•ŽŽŽŽŽŽ‰††‡ˆŠŒŽŽ‘’’’‘‘‘’”””“’’“••’‘“”“’”–™š–“’”––•—™™—”•–—˜˜˜˜˜™šš››™——”•›žžœ™˜šžžœ›ššš™›Ÿž››žœœžž¡¢žžŸ ¡¤¤¢ žžŸžžžŸ ¡ ¡¡ ŸžžŸŸžž Ÿžœ˜—šœš•˜ª¡vp_QK9".7<DJPTVY\]^]\[\_bccdggggknprsuvvvuuwwutuvvvtrtwwvutsstuwyyyyxwvtpmlmnotwvuutlZ;.HSUP;&)Tq€€{}‚ƒƒƒƒ„‚€‚ˆŠ‰ŠŠŒŒ‹‰‰ŠŠ‰‰ŠŠŠ‹Š‰‡†ˆ‹ŠŠŽ‘’‘‘•–‹ŒŽ‘Œ†‡ˆ‰‹ŒŽ‘‘ŽŽ’”””““’‘‘“••’‘“””“”—˜š˜–”–˜—•—š›˜•–———˜——––—šœ™™™˜––™œœœ›™™šœœš™™™š›žžœ›žœŸŸžžŸžŸ¦¤¢ŸžŸ¡¢¢¢¢££¡ ¡¡ Ÿ£££¢¡¢¡ ŸŸŸžžžŸŸŸž›——ššš”’¥¢qc^PG4"/7@HKPUXY]^^^]\]_abdfiigilnpqtwxwwuvyzxwwwwvtstwxxwurpqssstvwwxwuqonnoqsvuuuun^@!8OWXR;&%F[otwx€†…€€€ƒˆ‹Š‰‰‹ŒŒ‹‹Š‰††‡‰Š‹Šˆ‡†ˆŠ‹‹Ž‡‰Š‹ŽŽŽ‹‰‹ŽŽ“’ŽŽ“”“’’‘’““‘‘“–•””–˜™™™›œš–’”›™—–——––——–•”•–‘Œ–—––˜™šššššš››š˜˜™›œœœ›œžžžž ŸŸž›œ¥£¡ŸŸŸ¡¢£¢Ÿ¡¢¢¢££¡¡£¤¤¢¡¡ ŸŸŸ ¡ Ÿžžžžžœššš™–“Œ’•nd\N@,#/:CILQVXY[]]^]\]^_`cfiihjlmnpswxwvuvwxvvvwwvttuxzyxxvssronopqsttsrrqoprstttttoaE&@T\\S;%0DQ]adn}†„}~€€„‰ŒŠˆˆŠŒ‹Š‹‰‡††‡‰Š‰‡†‡‰ŽŽŽŽ‹ˆ†‡‹ŽŽŽŽŽ”’’‘““Ž‘’“’‘‘’’“’’’’‘Ž”—–””–˜™™šœ œ”’“——————––——–••••”Ž‹–—˜˜—˜š››šš›››™™›œž™šœžžžžŸŸŸž›˜§¤ ¡¡¡¢£¢Ÿ ¡¢£¤¢ ¡£¡ ŸŸŸžž ¡£¢ Ÿžžžœœ›š˜–”–Žb`YK;&$2=EJMRVXY\]\]]]^^^`cfiihjmmnoqtvvwvvuvvvwxxxvvx{{yyzxvvtpnpqqqrrrsuuqpoopsssspeK +DX__S9# *7BHKR`nqpqsz‚…‰‹‰‡‡‰‹‹‰‰‹‹‰‡‡‡ˆ‰ˆ††‰‘ŒŒŒ‹ˆ‡Š‘ŽŒ‘‘’’””“‘“’‘‘‘““‘ŽŽ’’’“”““””–™˜—••—˜˜—˜œœŒ‘’’”••–——•”””•—˜˜——š›šš››š˜˜šœ›˜™œœ›š›žžš•ŸŸžœ›œžžžžšœ žŸ¡¢¢¡¢¢žž ¡¡¡ŸŸ¡¡™šŸ Ÿžž ¡¡ŸžŸžžœ›œžžœ›ž››[_TF6"#3=CHMRUXZ\]\\\]^^^`befffgjjkmoqtvwvusuvvwxxxxyz}|yxxwwwvssuwvsqqrsvvspmloqrrrqhR!0I\b^Q7 %,39>AKU\ffj…‰‰Šˆ††ˆŠŠ‰ŠŒŠˆˆ‰‰‡ˆŒ‘’‹Š‹‹‰‰‹ŽŽ‘‘‘’’ŽŽ‘‘’””Ž‘““““’’’ŒŒ’’‘“•–•—˜šš›™——˜˜–——˜—‘’“”””•–˜—•’‘”˜š™˜™› Ÿœš˜™›œœ—šŸŸžœ››œ˜’“¡Ÿžžœš›žžŸž›žœŸ ¡¡£žž ¡¡¡ŸŸ¢¡› ¡ žœœžŸ¡¡žžŸž›š›ž›™ž—”ˆ\YMA2$3=BGLSWXZ\]\\\]^^^`bdeddfiihkoqstuuutuvwxzzzzz|~}zwvwwxxvvxyxwwvttuvspnopqrrsrjV5P`c]Q7 $(+-2<BXfm†‡‡‡†……ˆ‹‹‹Œ‘‘ŽŠ‰‹‹Š‰Œ‘‹‰ˆŠŒŒ‹ŒŽŽŒ‘’’‘ŽŽ‘’’‘““““’’’Ž’•”’”–––——˜™›š˜˜˜–•—˜—–“’“•••••–™œ—“’•œš—˜š›œœœ™˜˜šœŸŸŸžœœœœ™”––žœœœžŸŸ žžžžžŸ ¡ › ¡¡ ¡¡Ÿ ¡¢ žžŸ ¡žœœžž›šœœœ›˜–˜•‹yaTI>.%2;?CIQWXY[\\[\]]\\^_acccegggjlnqrrrsstuvxyyzyyz{{xuuwxxxxxxwvxyxtrsutqooqqqqrpj[<Uce]R7!$+6B[l†……†„„…‡Š‹Œ‘‘ŽŒ‹ŽŒ‹ŒŠ‡†‰ŒŽŽ’““‘ŽŽ‘’‘’““””“”–“‘˜™˜•••–—–”’–šš˜˜™–•˜šš—••˜™˜˜––˜š›œ—“™œœš–—˜™œœš˜—™œŸ ŸŸžœ›š˜”–™›œžŸŸžŸ Ÿ ¢¡žŸ œ¢¢¡¡¡¢¢¡¡¡¡¡ ¡ Ÿ žœœœ›œœœœœ™š—„jaRH;)(4<?CIOTWXZ[[[[]]\\]^_aabceghijlopqrttuwxz{{zyyxyywttvwvvwwvuuwyyvrtvvspnprrrqoj`&BWeg_Q6 %.AYi†……†……†ˆŠŒŒŽŽŒŒŽŽŽŽ‹ˆˆŠŒ‘’’’’‘‘““‘ŽŽŽŽ’””•–˜™™—•–™œš—”“”•”’’•™˜—˜™•”š›—–™›œš™——™š›››œœœ›˜–––•™›š™˜™œœ›œžŸŸžžž›šššŸœ›œŸŸ Ÿž ¡ œœœœžŸ ¡££¢££ ¡¡¡¡¡¡¡¡¡££ ŸžžžŸŸžœœœ›œž¡£ž”~f^RK<$(4<?CHMQUWYZZZZ[[Z[]]]^`bbcfhijkmoqsuuwxyzzyxxxxwwvttvvutuusstuvwwvwxwuqooqssqoka.FWdh`P7 "*?Xh„…‡ˆ‡†‡‰‹ŽŽŽŽŽŽŽŒŒŒŒŒŽ’”’’“’‘Ž‘‘Ž’•–˜™™™˜—–—š››˜”““”“’’”˜˜—˜˜•’›™—˜š››š™˜˜™šš›œœ›™™˜–“—™š›››››šœžŸŸžž››› ¡¡žœ›œžžžŸŸŸ ŸœœŸ ¡¦¥¤££ žŸ¡¢¢¢¢¢¢£¤¤¡ŸžžžŸ ŸžžœœžŸ ¨ ‘zb[RL=!'3;@DIMPSWXXZ[[[[YZ\]\\_a`bfijklnoquxxy{{zzyyxyyyyyxyyxvtuwvvxxxxyyxwwuqpqrtutrnc#8LZeh`N6 "*@\m†‹‹‰ˆˆ‰ŒŽŽŽŽŽŽŽŒŠ‹ŽŽ‹Œ‘’‘‘ŽŽŽŽŽ’’“•–—˜˜–‘““”••™™—•””“““’“—˜˜˜—–—šš——˜™™™™™™˜™™˜˜˜™˜˜™š›œ˜—™ššœ›››š›Ÿ Ÿœž¤£¡žžžœ››Ÿ ŸŸ¡¢ ŸŸŸ ¢¥¦¤¢¢¡žŸ¡¡¡¢£¥¤¤£¡ ŸžŸ ŸžŸŸŸžœœ ªŸscXPL:$/7>DHMPTWVWZ\]]\YXZZZ[]__afijklnorvxxyzzzzzzxxyzz{{|{{xvuwwy{|{yxxwvvtpoqrsttspf+?R_gh_N6 #*?^sŠ‹‹‹Šˆ‰ŒŒŒŽ’‘ŽŒ‹‹Š‰ˆ‹‘’‘ŽŒŒ‹‹‹‹Œ‘”•–––—˜—•‘‘“””–—–•””““”“”–—˜———˜™˜——˜˜˜™šš››œ›˜——–••—šœœ›› œœœš›žŸŸœœžž¡£¡žžŸŸžœ›œŸŸ ¡£¤¢¡ Ÿ ¢¤¥£¢¡ Ÿž ¡¡¡¢¥§¦¥¤¢¢£¢¡ žžžŸŸžœ›œœœ£›ˆ^`UMG4#+2=EINQTUUX\]]]]ZXXYZ[\^`cgjjlmoqtwyz{{{z{|{yy{||~~}}|zxwwvwy||zyxwvwvrpprssssqg%#2GXejh]N6 ")>\s‹ŒŠŠ‰‡††ˆ‰‰‰‹‘ŒŒ‹Š‰‡…‡Œ‘‘‹ŒŽŒŒŒŒ‹‹‹ŒŒ‹‰‰‹‘“—––•”–˜—”’‘“”””•–•”“’’”••–——––—˜—––————˜™š›œœš™™˜–•—™››ššžžžœœššœœœš›œž ŸžžŸžžŸžŸŸžžŸ¡£¤¢¡¡ŸŸ¢£££¢¡ ŸŸ ¡¢¢¢£¥¤¤£¢¢¤£¢¢ œžœœœššž•}KZRJD.")1<CFKOQRUY\\[YZZYYZ[[\^aegijklorsuy{}}}|}}{z|}~~~}|{yxxwwvx|}{yxwvwwtqoprrrrqj0$7GYcklg\M4 (=YpŠŠˆˆ†…„„…„…ˆŠŽŽŒ‹‹ŒŒ‹‰†„‚‹ŠˆˆŠŽ‹Š‰‰‰Š‹Œ‰ˆ‹’”••”“•——–”’“•••”•˜–””””•–——˜˜—––——••–••–˜˜˜›žŸœ›––˜™™™™šœŸŸžœžŸœš™—™œŸ›››œž œœŸ ŸŸ¡ žž¡££¡¡¢ žŸ¡¡££¢¡ ŸŸŸ¢£¢¡¡¢¢¢¡¡¢£¢¢¡ŸœžžžœŸ ”{PUOG>*(/9@BEIMPTXZZZYY[\[[\\]_beghjkmqtuuy~€€}}~~}}|zyxvuuvz~~|{yvuvvtsqqstssre=!=TWblomg[K3(=Xm…‡„„ƒ„………„…‡‹ŒŠŠ‹ŒŽŽŠ†ˆˆ†‡‰Œ‹Š‰‰Š‹ŽŽŽ‹ŠŒ‘’““””•—˜—–””••”“”—–•—™––—˜˜™šš˜”˜š—––••—™˜••”•œœš˜™œš™™š›žžžŸŸžŸŸ›™˜™¡¡ž˜™™›ž¤¦œœž žž¡¢žžŸ¡¤¤¢¢¢¢¡¡¡¡ Ÿž ¡¡Ÿ ¡¡ŸžŸ Ÿž Ÿœ™š œwZSMD9&&.6<>?DIMRWXXXZ[\\[[[\]^_acgjlmosuvy}€‚‚€~~€~|}|ywutttw{}|{zyvuutssqpqrstsgI,.Ybkpssng[K4(>Yo‡ƒ‚‚„……†‡‡ˆŠŒ‹‰Š‹ŠŒŒŠ†ˆ‹ŽŽŒ‹ŒŽŽ‘’’’‘‘’””•••–”“””““”•–˜š›™˜˜˜˜™šš™˜› žš˜•”˜›˜•’’Ž”›žœœ›œ¡Ÿššš››žžžžžŸ Ÿ›› ¤¢ ›˜š˜ž¦¨¢ŸŸŸž¡¢¢¡ ¡¢£££¢¡¢¤£ žŸ Ÿ ¡ŸŸ¢£¡ŸžŸŸŸ ¡ žŸŸŸœœœš—š’k]QKA3!%-5;<>DINRTVVWZ\]]]]^^^^^_bfjmmnrwy{}~ƒƒ€€~€~~}zwuvwvxyzyyyyxvtsstrpoprrqiY99Zjvwxuog[J6 (?]u‹ƒ„„„………‡‰‰ŠŠŒŒ‹ŠŠŠŠŒŽŽŒŽ‘‘’’’’’‘‘’“””““’‘‘‘’’’‘‘’’“’’“—šš™——˜˜˜˜˜—˜›žš—•”—™—•’’‘“—šš››žššœœ›šžžŸžž¡¤£¤£šš¡§©¤ŸŸŸŸ¡¡¡ŸŸ¡¢¤¢šŸ¢¢ Ÿ¢¡¡¢¢££¢ž¤¤ žŸŸŸ ¡¡Ÿžžž›œœ››žnaZOJ@.#-5;<?GMRSSTVXYY\]]]^^^^^_bgilnoquy{|~‚‚‚€€€~|zyyzxxyzyyyyxvttuuurooppolbN &D`n}yuogZH5!(@^xŒ„…†‡‡†‡‡ˆŠ‹Š‹ŒŒ‹ŒŒ‹ŽŒŒŒŽ‘““’’‘‘’“’‘‘’““”““’’”••“‘‘‘‘“““““““—™™˜˜˜™˜—–•—šœ™˜–—˜˜˜•“”•—˜˜šššš›žžœšžžŸ ž›œž ¡¢£££ žŸ£§¤¡œžžžžŸ ¡ ¡£¤¢œž ¡ ŸŸ¢¡¢£¤¤¥¤¢£¥¥ œž ŸŸ Ÿžžœœ›ž¢’haYNH>*!+5;:>HOSTTVYYWW[^^]^_`aabfjkmpttv{}~€‚‚€€€€~}||{zxyzzyyxxwvwxxxwtrpoooiZ)Gar{}vrohZF3'?^t‰…†‡ˆˆˆˆˆˆ‰ŠŠ‹ŽŽŽŽŒŒŒ’““““‘Ž‘’’’‘’””’‘Ž’“”–“‘‘’’‘’••”••””–˜˜™š›››š™—–˜šš˜˜˜™™™š™—˜›œš›œ›š›žŸŸžœ›¡ ŸœŸ ¡ ¡¢¢ ŸžŸ¢ ŸžžžŸ ¡¡¢£¢¡ ¡¡¡¢££¢¡¢£¥¥£¢£¤¦¡ž ¡ Ÿ ¡ žžžžžœ—˜¢bdXNH<'(165:FLORTVYYVVY]^]^_acddhlmnquwy|~€‚ƒƒƒ‚€€€€€~~}{{{xwwxyxwwwwwz{yxwtqoool`+Hary{upmhYE0&>[o‰†‡‡‡‰Š‰‰ˆ‰‹ŒŒ‘ŽŽŽŒŒŽ‘““’ŽŽŒŒŽ’”•”““’‘‘’’‘‘““’”’“•••””••–—˜™›œœ›š™˜—–—˜—˜šœš™œŸŸ žœ›ž Ÿœš›žŸšœœœŸ¢¡ŸŸžžŸŸŸ ¡¡ŸŸ ¡¡ŸœžžŸŸžžžŸ ¡¢¢ ¡¢¢¢¡¡¡¡¢£¤££¢¢¡žŸ¡¢ žŸŸ ¡¢¡ŸŸ ŸŸžžžš˜›ˆ^aVMH;##*04;DHJMPTYYXY[]_^^`cfggjnpqrux{|}€‚„„„ƒ€€€}}}{zyxvvxyxvvwww{~|zwurpopne& )B`mtxtnjeXE.'=Zq†…†‡ˆ‰‹‹Š‰ˆŠŒ‘ŽŒ‘‘ŽŽ“’‘‹Œ’”””“““‘‘’‘‘“““”’‘“““’’“•—————›œ˜——˜˜–•––˜šš™—™ Ÿš˜ œ››–™››Ÿ¡ ›œœžŸ ¡¢¡ ŸŸ¡¡ œœžžŸ ¡¡¡¡£¢ Ÿ ¡££¢£¤££££££¢¡¡œœžžœ›žŸŸŸ¡¡Ÿœœž£¢•~\^SKG8$,4<FJKLNTYZZ[\^___aegghjmppquxyz|~ƒ……„€€~}|}|ywvvvvxxwvuwxwy|}{wtqppqoh4!$;[fourlhcXF-(<Xo€‚†‰ŠŒŒŠ‰‡ˆ‹‘ŽŒ‘’“’‹‹ŒŽ‘’““’‘†Ž’“’’“”””“’‘’’’’’•”““‘‘’’’’’””•—˜”““•—–––——–•––—›š™˜˜š ŸŸ››¢¤ ž››œ›š››› ¡¡›žŸŸ ¡¡¢£¢¡ ¡¢¡ŸŸŸžœœ ¢£¤¢¢£¤£¤¡£¥¥¥¦¦¥¤¤¤££££¤ žžŸŸ› Ÿž ¢¡Ÿœ››œœžŸœ›Ÿ¢•zbZPHE5 +6@IMOQSWZ[\^_`acceijiijloptwxyz}€‚„†††ƒ‚~}|}}|yvuvvwxxxxxxxvvz~~zvsrrrpdD*1T^hmkhd[P@)*=Wkw…‰‹Š‡††‡‰‹ŽŒŒŒŽ’““‘‹ŒŽŽ‘“””’’—•’‘“”••”“’’“’”š—”’‘’‘‘’”——•”’’’’““”•—––––––˜œœšš™™œ¢ œšš¡ª£š˜˜šœžž ¢¢£ ŸžžŸ¡¡ ¢¢¢ ¡¢¡¡¡ žœŸ£¤¤¢¡¢¤¥¤££¤¤¤¥¦¥££¤£¤¥¤¤¢¡¡¡¡¡¢£ ¡¢¡ Ÿžœœž žœš™›“x^WNGD4)6AINPSUW[\^_`abbdgkjgffimpsvxyz|€ƒ…†‡‡†…ƒ‚~~}{{{{zwuvvvwwxxyzwuty~}zvttrpgT2&HY[__]XOD5! +?Vhv‚ˆ‰‹Œ‹ˆ…†‡ˆˆŠŒŠŒŽŒ‹Ž’”’ŒŽŽŽŽ’“’‘‘’”–™–‘Ž’••”“’‘’’’”š˜•’‘““’’“–˜˜–”“––“‘“••–––––—šžŸžœ™—–”—™šœ ”•–™›œžŸŸ¡¢££¢ žž ¡ ŸŸŸŸŸŸ ¡ ¡¢¢¢ŸŸ £¤¤¢ ¢¤¥¤¢¢£¢¡¡£¤£££¤¤¦¦¤¢£¤¢¡¡¢£¢¡¡¢¢¢¥¤ Ÿ ›’…q`TNGC2)7BHLOSUX\_`abcccdfhgdceimpsvy{{}€‚„…ˆŠ‰‡…ƒ€€|zxwxxvuvvvwwwvy{zwvy|~~|wutrqlcC*:CFGHF>5(,A\p{„‰‰‰‰ˆ†…†ˆ‰‰ŠŽŽŠ‰ŒŒŠŠŠŒŒ‘Ž’“’“”•–˜–‘ŽŒ’““‘’““““˜™••–˜•”“”•–——•”›Ÿžš•“™˜———————™žŸž›˜–•—™™š›™——˜™šš™™Ÿ¡¡¢¢¢¡žžŸ ŸŸžŸŸŸŸ ¡ Ÿ¡¤¤¢ŸŸ¡£¤¡ ¢¤¤¢¡¡¢ Ÿ ¡¢££¤¤¤¤¥¤¢¤¥¢ ¡¡¢¢¡ ¡¡¢¥¡¡ š™šœ ›ŒscZOKE?/'5@DHLQUX\^___abbaacdccfknqrvz{|}€‚ƒ„ˆ‹Š‡…ƒ€~|xvttsrrstttuuuvyzzzz{}~ywtstpgR%*+-/0,%+Bazƒ…ˆ‰ˆ‡†††‡ˆ‰‰ŠŒŒŠ‰ŠŒŒŠˆˆˆ‰Š‹‘‘ŒŽ’””•–””•–‘ŽŒŽ‘’’‘’“•—–—›š˜˜˜™–””•–•••”“œ›™˜˜›››š™˜˜™œŸŸ›™—”‘•›››šš™˜˜šž›™š¡¡¡ ŸœœœœŸ ¡¡¡¡¢¢¢ Ÿ£¤£¡ŸŸ¢¤¢¡¡£¤¢¡¢£¢¡¡ ŸŸ £¤£¡££££¤¤¢¢¢¡¢¢¡ Ÿ¡ŸŸ ˜–™šž™‰j_SIHB<+$4=BHKPUY\]^^^_`a`_acdehlprswz{|€‚ƒ„„‡Š‰‡„ƒ€~}|zwurommoqrstuuuuwyz{{{|~{zvttrk[)Be‡…ˆŠ‰‡†††‡‡‰‹‹ŽŒŠ‹ŒŽŽŠ‡‡‰‹‘’‘Œ••–——•‘‘‘Ž‘‘’’“””–˜™š››™™™—•””•”“’’’”›™˜˜˜™šœœœ›ššš›žžœ›››œŸžžœ›š˜˜™ ¢¥œš›œœ›››››œœ›¤¢¡¡¢£¢¢¤£Ÿ›Ÿ¡¢ Ÿ¡££¢¡¢¢¢¡£¤¤£¢ ž›ž¢£ ¡¢¢¡¢££¢¢¢££¢ŸœŸ¡¡žœš˜›–…h`PIGA9(!2<BHLPUY[[\]\\]_a`acfhilqrswz{{~ƒƒƒ„†ˆ‰‡…ƒ|{yvsrpmklppqsvwxxwwwwxxx{}}~~yutqmc*(AfŠ†‰‹Šˆ†‡ˆ‡‡‰Œ‹ŒŽŒ†‡‰‹‘“”•ŽŽŽ•–•–––Ž‘’’“”–••—˜™ššš™˜—•••––•’’‘’•——˜™™™šœžœ›š›œœššœŸžžŸŸ›™™š¢¡Ÿ››œ›ššœŸŸŸš™¥¢¡¢¤¥£¢¤§Ÿžž £¢Ÿ¢££¤£££££¤¦§¦¤£ Ÿ ¢££££¢ ¡¤¤£¢£££¢ žœŸ¡¡ŸŸž›˜›’ƒl_QKG@7&.:AFIMRVXZ[\\\]`abbdikjkpsux{|}~ƒƒ‚„†‰‰‡…|yyvspnmmmosssuxz|}}|xuuvx{|{}~{wtqnk>)Af~Œˆ‰‹‹‰‡ˆ‰ˆ‡‰ŒŽŽŒŽ’’‰ˆˆ‡“”•‹Œ‘–•““’ŽŽŽŽ’”––––——–—˜˜—–•””•–—–•”“”•–—™››š˜™Ÿœ››š››š˜˜šœžžœœž Ÿœ™™š¢Ÿœ›œœœž ¡¡ £¤¢¡¢¥¥¢¡£¤Ÿžžžž¥£££¢¢£¤¤¥¥¤¥¦¦¦¦¤¡¡¡¢£¦¦¦¦¤¢¢¤¥£¢¡¢¢¡ ŸžžžŸŸœ ››¢„m^SMH@5$*9>ADJORVXZZZ[^`abbeikijmqtwxz|~€‚ƒ†‰Šˆ…€zvutrolklmquwvwy|~€zvuvy{|{|}{yvsphR'!+BdyŽˆŠ‹‰ˆˆ‰‰‡‡ŠŽŽŽŒŽ’’‹‡ŠŠ‘’”‚‹Ž’•’ŒŽ‘‘’•–—––••––••–——–””–—––––••–—š››š’‘•™š›œœ›››š™™›ŸŸœ›œ¡¡žš˜™šžœž ¡¢¢¢¡¡¢£¢¢¡¢¦£ ¡¢¡žžžŸ££¤¤£¡¡¢¤¥¥¥££¢¡¡¡¡¡¡ ¢¤§§§¥¥¥¥¥£¡ ¡¢¢ ŸŸžžŸœ›™™¡‰|e[RLG?3"(8;=CINQTWXXZ\^``bcfjjijknrvwy|€‚ƒ„†ˆŠˆ…€yusqomllllpuyyz}€€€~zxy{{{{{{zyxwup`>"+BatŠ‰‰ŠˆˆŠŠ‰ˆˆ‹ŒŽŽ‘ŽŒŒ‘‘‘‘Œ…ŠŒŒŠŽŽ‹Š‘‘‘’’’”•••”’““””••–—–””••””••”•–˜™™™”‹Œ’•—˜žœœœœššœžŸžžŸ Ÿœš™œžž¡¡Ÿž ¢£¢¡¡¡ Ÿ ¡£¢ Ÿžž ¤¢¢£¡Ÿ ¢£¤¦§£Ÿ ž ¢¡žž Ÿ¢¥¥¤¤¥¦¥£ŸŸŸ¡¢¢¡ Ÿžžžœš˜—¡†zeXPJE<0%48<CILNQTTW\^_`abdgjjjklnruwz}‚„…‡ˆˆ‡…€{wtpmklmmmptx{}€‚‚‚‚‚€}{{{{{{|{yxy{yvkR"+A_q„…‡‰ˆ‰ŒŒ‹ŒŽŽŒŽŽŒŒŽ‘’’‘Œ‡‰‹Š‹Œ‹‹‹ŠŠ‘’‘“••”””•”’ŒŽ‘”•“’“““”““““””•—˜—––—–•••™žŸžœœ›››žŸŸ žžžžžŸ¡¢ Ÿ¡¤§¢ Ÿ £¤¢¡¡¡¡ŸŸ ¡¡ ¡¡ ¤¤¢¢£¡Ÿ ¢£¥§§¡¡¢£¤¦¦¢žž ¡¢£¤£¡¢¤¥£Ÿžž ¢¢¢ Ÿžœœ™™£ˆz`SNGA8-"06=CHJLOSUY^`abbdgiiiknnorvx{}€„†‡ˆˆ††„€{wtollmoqsuvx{~‚„„„ƒ}{{{||}|yvy}}xtl""*>\r„††ˆˆ‰ŽŽŽŽŒŒ‹ŽŽ”“’‘’’‹Ž‰Ž’“”–˜—•””””‘‹ŠŽ“——”””“’‘‘’’“”•–˜™—””•˜šœ™š™ŸŸžœœ››œœžŸŸžœ›Ÿ ¢¤¤¡Ÿžž¨£¡ Ÿ ¡ ¡¡ ¢£¢¡¡£§¥¤££¡žŸ¢£¤¦¦¥¤¤¥¥¦¦¥¢¡¢¢¢¢¤¤£ £¥£ŸžžŸ ¡¢¡ žžžš ™qWOKF?5*+4<AEILNRW[^``abehgegjlmoruwy|~€‚„…†‡†…„‚~yuspopqqruvwy{‚„……†…ƒ~zxy{|}|yxy|}zyt!!(;Xr†‡†‡†ˆŽŽŽŒŒŽŽ‹‹”•“‘’“’“’’‘‘’––““““”–˜—•““”“Œ‰’–˜—–––”’‘‘“““•———˜˜•““”šžžŸžœœ››œžžšœžŸŸŸŸŸŸ¡¥£¡Ÿ—ž¢¡ Ÿ ¡¢¢¡¡¢¡¡£¤£¢¢£¥¦¦£¢¡ ¢¢¢¤¥¤¤¤£¥¦¦¦¥¥¥¥¤£¢££¢ŸŸ¢¤£ Ÿ ¡¢¡ Ÿ ŸžŸtm\NJF@6''0:?DIMNRX\^^^`aceecfhhjmrvwy|ƒƒ„……„ƒ~ytrqrstussux{}€‚„†††‡‡†„}zxxyzzzyyz}|zu@&:Wt‹‡‡ˆ‡ˆŒ‘‘ŽŒŽŽŽŽŽŠŠ“”’‘““““”“’“”•••”’‘’–—•’‘‘‘Ž–—–••–—–””•–••–˜—–—˜—•“““—œ››œž›ššš˜™™šžœŸžš›œŸŸžžŸ¢¢ Ÿ›˜œ¢¡ ŸžŸ¡¢¢¢¢¢¢¢¢¡ ŸŸ ¢¤¥£ ¢¤£¡£¤¤¤¤¢¤¥¥¤¤¥¦¤£¢¡¢¢¡ ¢£¡¡¡¢¢¡¡ ŸŸŸžœœ› ‘rl\NIGB6%%,5;AFILQY[\\\_aabcbefefkptuw{~€€ƒƒƒ{wtrqtvwvutvz}€‚ƒ„…‡†‡‡‡†ƒ€|ywwwxyywx|}}ƒ`7%;\x‰‰‹‰ˆ‹ŽŽŒŠŠ‘’’””’’’‘“•”“‘‘’”–•’‘’“’Ž•—•“”–—˜———˜˜˜˜š™–•—™˜–””—™™™›œœš™™–™ššš žžŸž˜™šœ ¢¢¡žžŸ Ÿœ¡£¢Ÿžžž ¡¢¢¢££££ ŸŸ ¡¡¢¥¤¡ ¢££¢¤¥¥¥¤£££¤¤¤¥¦¤£¢¡¢¢¢¡¡¢¢ ¡¡¢¡ ŸŸŸŸŸ›››¡”jfZOKGB5!"*3:?CFKRY[[[\^`abbbccdglpstw{€€‚ƒ‚|xvsquxxwuvy|ƒ„…‡‰‰ˆ‡‡‡„‚|yyxxwwvwz|~oU%=`zŠ‹Œ‹Š‹ŽŽŽ‘ŽŽŒŠ‹‹‹’””’‹“••“‘’“•”‘‘”—š••——•“”–—™˜––—˜˜™š›—”–™š™˜––˜˜˜™›œœŸžœ›šœŸ Ÿž˜™—›¢£¤§¤žž ¡¡¢¢£¡ŸžžŸ ¢£££¤£¡¡¢££¢¡£¤¢¡¡¡¡¡£¥¥¤£¢¢¢£¤¤¤¥¦¢¡ ¡¢£¢¡¢¢¡ŸžŸ¢¢¡¡ ¡ Ÿž››œ¢•a`XPLFA2'18<@ELRXZZZ\^abba`__agmqssuz~~€‚ƒ€~{yvsruxywuvz|}‚…†ˆ‰ˆ‡ˆˆ‡…ƒ}{{{yvssux{}|{l!'Aax‹Š‹Œ‹Š‹ŽŽŒ‘ŽŽŽŒ‹‹ŒŽ“’‘’•–•ŒŽŽ•–—–’‘’”‘Ž”——–•••”“”—™š™–”–™šššš˜•–˜šš™——˜˜™šœžžŸŸŸŸž›™›Ÿ žœš™œ ¢¤¥¤¢™ ¢¤¥¤¢¡ŸžŸ¡¤¥¤¥¥¤££¥ª¦¡¢££¢¢¡¡¢£¤¤£¢¢¡¢¤¤££¤¤¡ ŸŸ¢¢¡¢£¢ŸŸ¢¡Ÿ ¡¢¡Ÿž›œ¡¢•[\WOID?1$/5;AGNUZZYZ[]ac`][Z[_fmqrrtz}}~ƒ‚€}zxvuuuxzzywx{~„†‡ˆˆ‡‡‰‰ˆ†„‚}|{zvrsuwz}~€~W*Dcx‘‰Š‹ŒŠ‰‹Ž‹‹ŒŽŽŽŽŽ“™•“”–—•ŽŽŽ”——•’’’’“’–––”“”––—™š›™–”•™›™™˜š––˜™—–•–—˜šŸŸ ŸžœŸžš™›žžœžŸ ¢¤¤¢šœž £¤¥¤¢¡ Ÿ ¢¡¡¤¦¦¦§¦¤¢¤§©¢¡¡¢£¤¥¥¥¥¥¤¢¢£¢¡¢¢¡ ¡¡ ¡¢¤¤£¡¡££ ŸŸ žŸ ŸŸ››£œ‹R`WNGB=."-29?ELUYXVVX\__[XXY[_cjmoprx|||‚ƒ‚|yvvvvxz{{{yy}€‚…‡‡ˆ‰ˆˆ‰‰‡…„‚€~{xwvvwxy{}~}‚{Z!+EcvŒ‰Š‹ŒŠ‰ŠŽ‹ˆ‰ŠŒŒŒ‘’ŽŽ“••••––•’ŽŒŒŽ‘•–—•””’’’Ž”——••”˜™šš››š—•–™›––••“”˜™—––—˜™¡ Ÿ››ž›šžžžœŸŸž ¡Ÿ ££¢ŸžŸž¡£¥¦¤£££¤££¢ ¡¤¤¥¦¦¥£¤¥¥¢¡¡¡£¥¥¦§¦¥¤¢¢¢¢¡¢££¡¡¡¡¡¢¤¥¥£¡ ¢¢ žž››ž ŸžŸ¡Ÿœ›—ƒT_UMHC=,*17<BIPTTTSUWYXTTVY]aehklnsy|||~‚}ywxxxy{{|}||€‚„†‡ˆŠ‹ŒŒ‰‡…„~|yxyz{{{}~~ˆt!+BczŠˆŠŽŽ‹ˆˆŠ’”’Ž‘’““”••••”‘ŽŽ”–•”“’‘““‘”—˜™˜—››šš››šš—––•“““”’”—˜—˜™™˜™Ÿ Ÿœ››œœ ŸŸžœžžž ¡ ŸŸŸ¡ Ÿ ¢£¤¥¤¤¥¥¨¤¤£ Ÿž£¤¥¥£¡¡¢££¤¥¥¥¥¥¥¦¥£¢¢££¢£¤¤££¢¢¡ ¡¢£££¡ ŸŸžŸž›œž Ÿ ›—–…]]TMIC<+'.5:AGKMPSTRSSRQTX[_cghiknrvy|||€~|zyzyxy|}}~€€ƒ…‡ˆ‹ŒŒˆ‡†ƒ€€}{{{|||~ƒ<!*@c{ˆ‡ŠŽŽŠ‰‰‹ŒŒ’“”‘‘‘’“““““””’ŽŽŽŽ‘••”’‘““””•——™–•šš˜˜››š™–•”’‘‘“•”•—˜˜š››ššžŸŸž››œŸ ŸžœœžžŸ¡¡ŸžŸ £¤¤¥¥¤¥¤££¤¤£¡¡¢¤¥¥¥£ ¡¢£¤¦¦¦¦§§¦¤¢¡£¤¤£¤¦¤£¤¤£¢¡¢¢££¤£¢ ŸŸŸ ¡››Ÿ¡¡ žžŸ¡Ÿ’ˆb\SLF@9)$,3:@DILNRTQONORVZ^aefghlosuwz{|~~~}|{{{zwy~€€€‚ƒ~~€…‡ˆŠ‹Œ‰ˆ‡†…„ƒ‚€}{{}~€‚ƒ€}†h5!*@byˆ‡ŠŽ‘Ž‹‹‹‹‹‹‰‹’““’’““‘‘’’“’ŽŽ‘•–•’’”•—–“”™—•“—š——››™˜–•“‘‘’“••–˜——œœœžžžœ››žž Ÿžž›™›ŸŸ ¡ ŸŸŸŸ¡¤¥¤££¤¡šŸ¥¤¢£¤¥¥¦¥¢Ÿ £¤¤¤¥¥¥¦§¦¥¥£¢£¥¥¤¤¥££¤¤£¢£¥¥¥¤¤¤¤ ¡¡¢¢žŸ ¡Ÿœž ¡•Šƒg[SMG@8( *27=AFJKNQOMLNUZ]`ceedglnpsvyzz|||||}}|ywy~‚‚‚‚~||…ˆˆ‰‰‰ˆˆ‡‡‡†ƒ€~{{~€€ƒƒ€|{u`!*Acxˆ‰‹ŽŒ‹‹Œ‹‹‰‡Š‘‘’’ŽŽ‘””’““Ž‘’”••“’“•–˜—–—™˜–“‘‘–˜˜˜š™˜˜˜—•‘’’’“–›žž Ÿžžœœœœžž¡ žžžœ™šž ŸŸ ŸŸ Ÿ¢¦§¥¡Ÿ›žžŸ¦¥¤£¢£¤¤¤£¡Ÿ¥¦¥¤££¤¤¤¤¤¥¥¤¤¥¦¦§§¥¥¦¥¤£¤¦¦¥¤£¤¥£¢¢£££ ›œž ›…{eYTOHB9(&06=@DGILNMKLQY^`bcdddhllmqvxxy{{zz{}}|zy{}€‚ƒƒƒ‚€}{|€…ˆ‰ŠŠŠ‰ŠŠ‰‰ˆ„€}}‚ƒƒƒ‚‚}{~w!+Dez‰‹ŽŒŒŒŽ‹Š‹ŽŽ‘‘Ž’“‘’–•’‘“””•”“’”–——•”—˜˜•’‘•˜š›œš™™›š˜–”’‘“—¡ŸŸ Ÿœœ››šœžŸžžžœœžŸžžžžšž¡ œš›žŸ £§¦ š—’—›¡¤¥¥¥£¡ ¡¡¡¢¦¦¦§¥£¢£¤£¢¡¢£¤¤¦¦¦¦§§¦¦¦¥¤¤¤¤¢£¤¢¢£¢¢¡¢¤¥¢ ŸŸŸœžžœ~raWSOIC:'!.4:>@CFHIFGMU]``aabcehjjlptvvwyyyyz|}}}}~ƒ„„ƒ{y{~‚…ˆŠ‹‹ŠŠŠŠ‹‰†„ƒ‚~|~‚……„ƒ€}z(!-Fe|ŠŠŽ‹Š‹ŽŽŽŽŽŽŒŽŽŽŽ‘”—–”“’‘”–—–”’’”–——•”–—–”’‘Ž—˜™›œ›šššš™˜—“’’—šŸŸžžœ›››™—™Ÿ Ÿž›œŸ ¡ ¡ œš™œ Ÿ ¢¤¥¥£›˜™˜ž¤¤££¤£¢¡ Ÿ¢¦§¨§¥¢¡££¢¡¢£¥¥¥¦§¦¦¦§¦¦¦¥¦¦¤¤£££¢¢£££¢¢¤¥£¢ ŸŸŸžžžž££ƒq]TPMIB7%!/59;<@DECAFQY^``abbdghijnruvvwzyyz|~~~€€€ƒ……„‚}yy{ƒ‡‰ŠŠŠ‹ŒŒŠ‡„‚~|~ƒ„……„ƒ‚~…@ "/He}‹ŠŽŒŠŠŽŽŽŒ‹ŽŒ‹Ž‘“•––•”“‘’”——”’’“”–––•–––”““—šš˜š››š›š™˜™›—”–—™žœœœœœœš˜™ž žœžžœ›žŸ¡¡ œš›¡£¤£££££¤¥¤Ÿ ¤§§£¡ ¡¢£¢ž ¦¥¦¦¦§¦££¤¤££¤¨§¦¥¥¥¤¥¤¤£££¤¥¥£¢£¢¢£¤¥¥£££¢£¢¢ žž Ÿžœ›¤¡l`SOMJA5$-48:<?@?==FT\_a`ababdgijosvvvwyyz|~€€€‚„†…ƒ€|xwwz}‚…†…‡‰Š‹‰ƒ€~‚„„……„ƒ‚ƒZ4"/HeŒ‹ŽŽŽ‹ŠŒŽŽŽŽŽ‹‹‹Œ‹‹‹’““‘‘”••”““’‘’•–”’‘‘‘‘”—•–—˜˜––˜››™™›››œ›™˜š›™˜˜˜˜œžœžœœžŸŸŸžŸŸœœžžŸŸœœŸ¡¢£££¢¡¢¤¦¦£¢¢¤¤£¢ ¡££¡¢¥¥¤¥¤¥¦¦¥¥¤££¤¦¦¥¥¥¤££¢£¢¢£¤¥¥¢¡¢¢£¥¦§¥££¤¢ ¡ žžžœ›™ ›{g]ROMH@4"(17;<;:89?KW^`bbbb`_aeikotvvwwyz{}€€€€€‚……„‚€vrrvz}‚…‡‡‰‹‹‹‘‹ƒ€€€‚„…††…„ƒ‚€zhH"!,FdŠŒ‹ŠŒŽŽ‹‹ŽŽŒŒŒ‹‹ŠŠŠ‹Œ‘“”’‘““’’’‘‘“•”’‘‘ŽŒ‘•—˜˜—•–˜™˜˜š››œ›™™š›š˜˜˜˜š›œœžžœžžœŸ žŸŸœ››žŸŸžž ¢¡ ¡¢¤¦¦£¡¡¡¡¢£¢¡¡¡¡¡£¤¤£¤££¥¦§¥¤¤¤£¤££££¢¢¡ ¢£¢¢£££¡ ¡¡¡¢¤¥£¢£¤£¡ Ÿœœ›š™™ž•xg\QNLG?3!!+4997626COY_aba``^]`dgjnrtuwwxz|~€€€€ƒ……{vpmotz}~†‡ˆ‹Œ‹ˆ‰‹ƒ„†‡†…„„ƒ{] *Aa}ŽŠŠˆ‡ŠŽŒŒŽŽŒŒŒ‹‹ŒŽ’”“’‘‘’“““’‘’””“““Œˆ‘”–˜˜—––—˜—˜š›ššš™™™ššš˜˜˜˜˜šœœœžžžœ›œžŸŸžŸ ŸžœœŸ ŸŸžž ¡¡¡¢¢¢££¤¥¤£¢¢¢¢¤¤£¡ŸŸ¡£¤¤¤¤££¥¦¦¦¦§¦¤¤¤¤£¢¡ ¡£¥¥¤££¢¢¢¢¡ ¡¢£¢¢¤¦¤£¢¡ ŸžŸœššŸŸŽqiYNMKE<1%/554417IU]aba_^^^`cegkoruxxxy{}€€‚ƒ„……€tojimsy}~‚†‰Š‹ŒŒ‰ŠŒŽŒ…€€€‚†ˆˆ‡…„„ƒ‚ƒ‡o"*7`|Œ‰ˆ……†ŒŽŽŽ‘‘‘““”””““““’‘‘’””””’‘’“•––•–––—˜—˜™™˜™šš™˜˜š›™˜——˜™šš›››œœœœœœžžŸ¡¢¡Ÿž žžŸŸ¡£¤£¢££¢¢¢£££££¢£¤¤£¡ŸŸ¢£¤¥¥¤£¤¦¦¦§ªª§§¦¥¤£¢ ¢¤¥¥¤£¢¢££¢¡Ÿ ¢¡¡£¦¦¤££¡¡¡ Ÿœœœ¤‰mhWKJHC;.!+01023<MX_ba_]]]_acegimqvyywwyz|~ƒƒ„……ƒphfhmrwz|€„ˆŠ‹‹‹‹ŒŒ‡ƒ€€ƒ†ˆ‰‰‡…„ƒ‚‚x'" $-:c€‰ˆ……ˆŒŽŒŒŽ’’‘’““’“”••””“””““’‘“”””““•–•••••”••–˜—————™››—–˜›œš™˜˜šš››œœœœœž ¡žŸ ¡££¡Ÿžž ¡ žŸŸœ¢¦§¤¢££¡¢¢¢¢¢££¢¢££¢¡ ¡¢£¥¥¥¥¦§¦¥¤¦¦¤¥§¦¤££¢¡¢¢£££¤¤£¢¢££¢¡ ¡¢¡¢¤¦¦¤££¡¡¡¡žœœ¡”ieTIGEB;+(/.+/6BRZ_a`]\]^_acegilrxzxvwy{}~‚ƒ„…†……Œfacglquy{~„ˆŠŠ‹ŒŽŽŒˆ„‚ƒ†ˆŠ‹‰‡„‚€€~€F&#-?g‰‰ˆ‡ŠŒ‹‹Ž‘’’““”•–•“‘’’’”“Ž‘”••••–——”’”•–••”•––••–—™›š–•˜š›ššš›œœœžžŸ¢¦ žŸ¡¢£¢žœš ¡¡ Ÿ¢¥¥¤££¢¡¢¤£¡¢££¡¡¢¡¡¢££££¤¥¤¥§§¤£¡ œ ¦§¤¢¢£¤¥¥¤£¢£¤¤£££££¡¢£¤££¤¥¥¤£¢¡ ››šžŽ€e`SKGDB9('-*&,8FV^_`_\[\]]_adfimrwxwvxz|}}~€ƒ„…†ˆ†‡„YY`fjoty{~ƒˆ‰‰Š‹ŒŽŽŽ‹ˆ…‚‚‚ƒ…‡‰‹‰†„~~~†h=# +?f~‹ˆˆ‰‰ŠŒŒ‹‹‹Œ‘ŽŒ‘’‘’”••’’““••‘‘•–————˜™™”‘“–˜˜˜–•••”•–•–™˜——™››››œžŸœœŸŸžœžŸŸ ¥ žžŸ¡¢¡ ž›˜œž¡¢¢¡ž¡¤¤§§£¢¢£¥¥££¤¤¢¡¢¢¡£¤¤££¤¤£¢¥¥¤¢¡ ¡¢¥¦¥¤£¤¦§¦¥¤£¢¤¥¥¤¤¤¤¤¥¦¥¥¤¤¤¤£¡¡ ž›š›¡”‡f[QNICA8%%*($*:JY```_[[\]^_bcfjmquvuuwxz{{{~„†‡‰†‰~^U]dimsy}€„ˆŠŠŠŽŽŽŠ‡…ƒ‚‚„†ˆ‰Šˆ…ƒ~}~}Z(Bb{‹ˆˆ‰‰Š‹‹Š‹ŒŒŽŒ‘‘‘’’’Ž“”••“‘’“–˜™˜˜—˜™™•’—ž —””••••““•˜šš››š››œžž——ž ¡ŸœžŸ›žžŸŸŸŸŸžœžŸ¡££ œ›—˜¥¡Ÿ £¤¥¦¥¤¤£¢¢£¢¢¤¥¤¤£££¢¢£¥¤£¢¢¤¤¥¦¦¥¥¤¤¦¦¥££££¥¦¦¤¤¤¤¢£¤¥¤¤££¢¡ ŸŸ Ÿœšœœ’„dZROLFA7$"%%$,>MY]^_^\[\]^`abejlnruuuvvvxxy}‚…ˆŠ‰lYS\chkqx}€ƒ‡ŠŠŠŒŽ‹ˆ†„„„…‡ˆˆ‡†…„‚€~p *Baz†‡ˆˆ‰ŠŒŒŠ‹‹‹ŒŽŒ‹ŠŒ‘Ž‘’“‘Ž‘•––”’‘“”–™™™—•–™™–•”™ž—””–—–•““•˜™œœ›œœœž™˜œŸ ŸžŸœš›žŸŸŸž ¡¡ ¢¡£¤£ ™˜–œ ž£¤¤§§¥¤¢¢¢¢¢£¤¥£¢¢££££¤§§¦¤¤¥¦¦¦¥¤¤¥¥¥¥¤¢££¤¥§¦££¤¢ ¡£££¢¢¡¡¡¡¡¡ŸžŸšœ•ŒeZRNLGB7$"#%1DQX[]^^\[]^_abdgkmnpsuvvutvwz‚„‡Š‹’tdUR[bhmrw{ƒ‡Š‹‹ŒŽŠ‡†‡‡‡ˆˆ‡††‡…‚€€€€„1!-@e~††††‡ˆŠŒŒ‹Š‰‰‹ŒŠŠŠ‘’‘‘’’‘ŽŽ”–••“‘‘“•—™™™—“”™š˜——›ž›–””•–———•˜˜—˜šš›œ››œ›š˜žŸŸŸ ŸššœŸ ŸžžŸ¡¡¡¡¢¢¢¢¡¡Ÿ—Ÿ¤££¥¤œŸž££¢¡¡¡¢¡¢£¢¡¡¢£¤¥¥¦§¨¦¤¥¦¦¦¤¤¤¥¤¤£¢¢££¢¡ž¡¡¢¢¡¢¤¤£¢¡ ¡¡¡ žœœœœš˜Š{dYSMJDA7&"&4ITY[\\[ZZ]__`cehlnnnqstutuuvy~ƒ†ˆŠŒ–e_TT]dimswz}‚†‰Š‹‹Œ‰‡‰‰‰ŠŠˆ†††…‚~~€ƒR+!/Emƒ………†ˆ‰‰Š‹‹‹ŠŠ‹ŒŠ‰Š‹‘‘ŽŽŽŒ”••”“’“•˜šš™˜—“•™›™˜™››™’“”•––—˜˜˜šš—–˜™š›šš›œž ŸŸŸ ¡ šš¡¡ ŸŸ ¡ ¡¡¢¢¡¡¡¢£¢£¢¡¤¥¥¤¤£ Ÿž ¢£¢¡ ¡ ŸŸ ¡¡¢£¤¢ ¥§¦¥¥¦¦¦¦§§§¥¤¢¡¢¤£¢¡¡¡¡¡¢¤¦¦¥£¢¡ Ÿ ¡¡ŸŸžœœ›š ›‡v_VQMIDA9&")8LV[]]\ZY[_`acfhilonnqsttuxxww{†ˆŠ‹•WXSV`glptx{~‚†‰‹‹ŒŽ‘‘Š‰‰‰‰‹Œ‰…„……ƒ||…mA!/Er††††‰‹Š‰Š‹‹Š‰‹ŽŒŒ’“’‘Ž‹‹ŒŒŽ’””““”–šš˜˜—••š›™˜š›š–‘““˜™™˜˜——›¡˜–—˜˜˜˜™›ž ¡ ŸŸ žš› ¢¡žœŸ¢¢¡¡¢£¢ ž ¥£¢¢£¢££¤£¢¡ ¡¡¢¡Ÿž ¡ ŸŸ¡¢¢¢££¢¢£¦¦¦¦¦¥¦§¨¨§¥¤£¢¢¢¡¢¤¥¤£¢£¥¦¥¤£££¡žŸ ŸžŸ Ÿžœœ¢œ‚qZSOLGA@8% )9LW[]][YY\_acdghhjmoqqrstuxzyx{€„ˆŠ[SSYbhlpuy{~…ˆ‰‰‹Ž‹ŠŠ‰‰‹‹Š†„„„ƒ€|{}€‚a!/Fs†‰‰‰Š‹ŠŠ‹Œ‹‰ŠŽŽ‘•”•‘ŠŠŒŽ‘••””””˜›™˜—˜–•˜™™˜››š—”•˜™›™œž˜–—˜–•–™œžŸŸžžŸžŸ¡Ÿœœ ¡ ž ¢¢¢£¤¢ Ÿ ¦¤¢¢¢¢¢£¤¥£¡ ¢££¢¢¢¡Ÿ ¡¢¡£¥¤¢¡£¥¤£¤¤¦§¦¤¤¥¦§§§¥¥¦¦¤¡ ¡¥¦¥£¢££££££¤¤¤ Ÿ Ÿ ¡¡ Ÿ ™~m^PNKD>>6#*=OX[]\[ZY\^adfgghjmpttrrtuvwxy}„‰‹VPS[bgkpuy{}€ƒ†‡‰‹‹‹Œ‹‹Š‰‰‰ŠŠˆ…ƒ‚|{|u .Gq‡Š‰‰‰‰ˆ‰‹Š‹ŒŒŽ‘”•”’‘“ŽŽ‘–—•”“’‘–––—–•–•‹‰šž›š™™š™™œ£ ‘—œ—–˜™˜”•šžžœ››ž›ŸŸžžžž¡¢£¤¤ Ÿž™œŸ Ÿ ¡£¤¥¤¢¡¢£¢¡¡¢¡¡¡£££¥¨§¦¨ªª§¥¤¤¥¦¤££¤¥¥¥¤¤¥§¦¥£¡¡¤¥¤¢¡¡Ÿ¡¡¢¤¤¤£¡ ¡¡ ¡¦¤˜•{j]RPLFA=2 -@QXZZYZ[[[\^bdefhjmpttsrsttsux}…‰‰‘QPT[afjntz|}~„…‡Š‹ŒŠ‹Œ‹Š‰‰‰ˆ‰‰‡…‚~~€‚7.Gpƒˆ‰‰ˆˆˆ‹Œ‹‹‹‹“•”’‘•–“‘“–—–•“‘•–——–”“‘ˆ‡™š›››š™˜˜Ÿ˜™š™––š›šœŸ ¡Ÿžš™š››œžžžŸ›ŸŸžž¡¤¥¤¤›ž›š›žžŸ¡¢¢¢¢¡¡¡¡ ŸŸŸ ¢£¤¤¦¨§¦©©¨¥¤¤¥¦¦¤£¤¥¥¥¤¤¤¦¥¤¤¤££¤¤£¢¢££ ¡¢£¤¤¤¤¢¡¢£¢ Ÿž¡«¨›˜€l]TRNJD</0BPXZZY[]^\\^acefiloprtuttuvwz|‚†‰‰ˆu[QU\bhjmsz|~€‚ƒ…ˆŠ‹‹‹‹ŠŒ‹‰‡ˆ‰‰‰ˆ†„€~~‚ƒ‚„‹W*.Hp‡‰ˆ‡‡Š‘‘’‘ŽŒ‹ŒŽ’‘‘““’‘’——”ŽŽ’“””””“‘“••–”“’ŽŽ”—™š›š™˜——™˜—––••—šœœž Ÿ™™™šœžŸŸŸœ›œŸ ¢£¦¨—œŸŸžŸ¡¢£¡Ÿ žžž›Ÿ¢£¤¤¥§¦¦§¨¦¤£¦¨¨¦¤¤¥¦¦¥¤¤¥¥¥¥¤¥¤££¢¡¢£¥§¨£¢¢¢¢¤¦¤¢¢¢¢¡Ÿœ¢£–‘€k[RPLIB:/1BMW[[Z[]\\]^`bdfilnoortssuwz}€‚ƒ‡ˆ‡€kZRV]dhjkqx{|€‚ƒ…ˆ‰Š‹‹ŠŠ‹‹Šˆ‡‡‡ˆ‰ˆ‡…€~~ƒƒ„ƒˆm@!%%$#! .Hl€‡‰ˆˆ‰Œ“’“’Ž‘““Ž‘“—˜–’’“““”””“‘’“’‘’‘”•–—™™™š™˜˜˜—––”•——™›œžžžž ŸŸžœ™›žŸ ¡œœŸ¢¢¢¡ŸŸŸ˜•š¡¡žŸ¡¢£¢¡¢¢¡ Ÿ ¡¡£§ ˜¢¤¥¥¦¨§¦¦¦¦¦¥§©¨¦¥¥¥¥¥¤£££¤¥¨§¦¥£¡¡¡¢¤¥¥¥¤¤££¢¤¦¥¢¡ ¡¡¡œ †yhXPNID?:02FRY\]\[[[]^^`cdfjnonnqrqrux{~‚„„‡‡…wfWSZ`eiikqxxy}ƒ†ˆ‰‹Œ‹ŠŠŠŠŠ‰‰‡†ˆ‰‰ˆ…€‚ƒ„ƒˆ„c!'+-,+)(&$ -Cj~‰‰Š‰ŠŽ‘“’’“’ŒŒŽŽ•˜˜•‘‘“––•••”••”“’‘“’‘‘“’“•–••–˜šœ›ššš™˜š›œ›™˜™›œœ˜˜ŸŸŸžŸŸŸ ¡¡¡ žŸ¡££¡ ›—”ž¤ž ¡¡¡¡¢¤¤¡ ¡¢£§§©¨¢£¤¤¤¥¦§¦¥¥¦§¦¦¥¤¥¦¦¥¤££¢¢¢¡¥§§¥¥¤£¢¢£¥£Ÿ¡£¤¥¥££¤¤¢¡ŸŸ ¡ ž¨£zsaTLLHA<8-2HSXZ\[[[[]]]_cdeimmlnponquy{~ƒ„‡‡…sbTR[aehhkrxy{~‚„‡‰‹‹‹ŠŠ‹ŒŒ‹Šˆˆ‰Š‰‰†‚€ƒ„ƒƒ‰t$+/24542.)%!!,@h|„Œ‹Œ‹Ž’’‘Ž‘‘’Œ–š™“‘“••–˜˜–•––••“‘‘‘––“•—•”•–•••–›œœœ››šš›œ›™–—˜šœœ™˜šžžžŸ ŸŸ ¡¡¡ ŸŸ ¢£ ŸŸžžž›˜Ÿ ¡Ÿž £¤¤¢¡¡¢¢£¥§¨¨¦¦¦¤££¤¦¥¥¥¦§§¦£¢¤¦§¦¤¢¢¢£¡¡¦¦¥¤¤¥¥¤¢¢£¢ Ÿ £¦¦¤££¢¢¡Ÿž Ÿž««Š~p`RKKG@;6+4IRVX[\\\]_^\_bdehkllnpoorvy{~ƒ…ˆ‰n^POY_behlsx{~‚‚‚…‰‹‹Œ‹ŠŒŒŠ‰Š‹‹‹‰†ƒ~€‚ƒ……ƒ‹„B $*.148<<:5.)%! #,EgyŠŒŒ‡‰ŽŽ‘‘‘ŽŽ‘‘’•™˜•’“•––—™™•”––––•”•™œ™˜˜™—•••••””•š›œœœœ›š™šš˜•–››œœ–˜›žŸŸžŸ ŸŸŸŸŸžŸ¡ Ÿ Ÿ¡ ž£¢ ŸŸž¢£¢£££¢¡£¥¦§¥¥¦¥£¢¢££¥¦¥¥¦¦¤£¤¥¥¦¤£¢£¤¦§¨§¥¥¤£¢¢¢¢¢¢¡ ž£¥¥¤££¢¡ œžž®¤m_RKJGA;7-5JQUW[]\[]_^]_bcdfgiknpqrtuwx|…‡‰ˆaYLLW\^bgkrx{~‚€‚…ˆ‰‹ŠŠ‹Œ‹ŠŠŠŠŠˆ…ƒ€€ƒ„‡ˆ…ƒ…jA #&'%%(,.03:?@?;50+&%#""! !!$-Dex‚ˆ‹‹††ŠŽ‹‘Ž’’’‘’“•—˜—”•——––š˜–••–––••—™›š™˜˜—–•”””””•˜›œœœœ›•–˜›Ÿžžœ›™žŸžž ¡ ¡¡ ŸŸ žžŸžžŸ¡¡¡¡ ¢¡žŸ£££¢Ÿ—Ÿ¢£¤¥¤¢¡£¨©¨¡¡§¨¦¤£¢¢£¤¤¤¥§§¥¥¥¥¦¥££¥¦¨©§¦¦¦¤¢¡¢¤¤¤£££¤¥¥¥¤¤££ žŸŸžœ¤–‰ya\QLJGA<917KSWY\]]]^^_^`bcdfgilnqstvwxy{‚†ˆ‰†ˆZTKLU\^bhlqwz}}~€ƒ…‡ˆ‰ŠŒŽŒŠ‰ŠŠ‰ˆ††…ƒ‚ƒ…ˆŠˆ†zh"%),.00.++,,-/6>A?<971-*&#"! !!%-;e~‰‰‹Š…ƒ‡‹Œ‹ŠŒ‘““‘’“””–———˜˜–”’”——–––——––—˜˜™™˜—–•”“““”•–˜šš™˜˜—••—Ÿ¢¢ŸžžŸ¡¡ Ÿžž ¡¡¢¢¡¡ ŸžŸ Ÿ¡¡¡¢££¡¢£¡Ÿ ¢££¤¨§¥¥¥¥¤£¡¡¢£¤£Ÿ¨©§¥¤£¢£¤¤¦§¨§¥¤¥¥¦§¦¦¦¦¨¨¥¤¥¦¤¢¡¢§¦¥¥¤¤¤¤¤¤¤¤£¢žš¡¤¤¡žœ›’Œx_XQLIFC>;2!8MUY[\\\\]]^^`bccehjloqrsuwxxz„‡††‰_QKMT[^ahlpuxyyz|~€‚‚„‡Š‹ŒŒ‰ˆ‹Š‰ˆˆ‰ˆ…€ƒ„ˆŠˆ…u"*26998640.,+,2;=;8::62.)%# #,;l‰ŽŠŒ‹…ƒ†Š‹Š‰ŠŒŽ‘’’’’““””•—™š™•’”—˜———˜˜˜—–•–——˜—––––––——˜™š˜““”–••˜žžž››šŸ¡¡ žžžŸ¡¢£¢¡Ÿž ¡¡¢£¡¡¤¥¤¤£¢¡¡¢££¤¨««¨¨§¥¤¤£¢¡ŸŸœ ¨¨¦¥¤££¤¥§¨©§¦¤¤¥¦§¦¥¦¦¥§¨¥¤¤¥¤¢¢£¥¦¦¥¥¤££¥¥¤£££¡ž£££¢¡Ÿš‘‡x]VQKFDC>:19MVZ\\\\\\\^^_bcbdhjmpqrsuwxy{~‚„…ˆ†ZNJMTY]agknrttvx{}€€ƒ†ˆŠŒŒ‹Š‰‹ŒŠˆ‡‡‡…€‚ƒ†ˆˆ†7 %-6;>?><951,('+24115742/*'# ",=nŠ‹Œ†ƒ…ˆ‰‰‰‰‹ŒŽŽ’“’’’“““•—˜—•‘‘”˜™˜——˜™˜—–•••–———–——˜˜˜™™™˜˜–“’š™•™œ™™™›œžŸžž Ÿž¡¢£¢¢Ÿž ¡¢¡¡£¤¤£££¢¢¢¡ ¥§¦¦¦¦¤¤¥¤£¢£¤£¦¨§¥¤¤¤¤¦§¨¨§¤¤¤¥¥¥¥¢ ¥§§§¨¦¥¥¥¤£¢£¤¤¤¥¥£¡£¦¥££¢¢¢¢¢££¢¥¨ ˜„tcWTMECB=9/7JSXZZZZZ[\^]^`aadgiloqstuvxxz}ƒ‚†UIGLSW[`dgimprtwy|}~€‚…‡Š‹‹‹‹‹‹Šˆ‡‡†…ƒ‚‚‚‚…‡ˆ‡„ˆ‚V'"'-59<>?=950*%#$(((*0320,($" !->iƒŽŒŒ†„…‡ˆˆ‰Š‹ŒŽŽŒŒ““’’’’’“••–•’‘“—˜˜–––––––——–••–––—˜™˜™›œš˜™˜Ž›œ››œœššš›ŸŸœ ŸŸ ¡¢¢££¡ ¡¢¢¡¡¢¢Ÿ¡¢£¢¢¢ žž£¤££¤¤££¥¦¤¤¤¦§§§¦¦¦¦¥¥§©©¨¥¤£¥¥¤£¤£¢£¦§§§§¦¦¦¥¥¤£££¤¥¥¤£¤¥£¢¢¡¢££¢¢¡ ¡œžž…qaURKDBA=;/5HPTVWXXXZ\^^^`abdfilprsssuwxz~€‡LCBIQUZ`egimqtvxz|}€‚„„…‡ŠŽŒ‹‹ŒŒŒ‹ŠŠŠˆ‡…ƒƒƒ„…ˆ‰Š‡Š„c;#&*0479::61,'"!!"&-00,'#! ".E`yŠ………†‰ŠŒŒŒŽŒŠ‹Ž“”“““’’“””””“’“•––•“““”–˜™™—••••”•–˜™šœ›š›žœž¡ž››œœœœ›Ÿ Ÿž››Ÿ¢¢¡ Ÿž¡¥¤¡££¢¡££¡ŸŸ¡¡ ¡¢š¡£¤£¢¢¡¡¢¥¥¥¤£¤¤¤¤¦¨¨¨¥¤¦¨¨¦¥¥¥¦¥¤¤¥¤£¤¥¦¦¦¦¦¦¦¥¥¥¤£¢¦¦¥¥¥¥¦¤¢¡¡¡¡¡¡¡ ›”ˆ’•}n_SOKFC@?<-2EMRTUVVWY[\]]^`abdhloqqpooprw}ƒ~C=@HPUZafkosuvwz{{|€‚ƒ„……ˆ‹ŽŒ‹Œ‹ŒŒ‰†„‚‚ƒ„‡‹Œ‹jL#&(+.1231.*&! $)+)&# ".D_vŠŽŠˆ‡…ŠŽŽ‹‹’””““’’”••””•••––•”“’’“”—™˜——––•••—š›œžžœš›œžžžœ™˜š››œŸ¡¡Ÿ›› ¤£¡ŸžœœŸŸ ££££££¡ŸŸŸŸŸ¡¢¤¤£££¢¢¡¡¡¤¥¦¥¤¤¤££¥§¨¨¨¥¤¦¦¦¥¥¥¦©¨§¨¨¥¥¦¦¦§§§¦¥¦¥¤¥¦¦§¨¨¦¦¦¥¥§¢¡¡¡¡ ¡ š‘Œyl^SPKFB>;6(/BJORTUUWYZ[\]]_``cglopponkjmtz~ƒu>:?HQW]dimtz{z{}}}‚ƒ…†…†ŠŒŒ‹‹ŒŒŒŒ‰…„ƒ‚‚‚„‡‹Ž‰oW" "##%'())(&$ #&%#!!.FbyŽŒ‹ŠŠ‰‘“’‘‘Œ’•–••”“•—–•–—–––—–•“’’“”–———˜˜–•”–™›œ˜˜›™™š›˜•™™˜––•“Ÿ ¤¥ž™™šŸ£¢ŸœœžžŸ ¡¢¢¢¡ Ÿ ¡£¤¥¤¢¢¢¢ ¢¨¦¦¦¥£¤¥¤¤¦§§¦¦¤£¤¤¥¥¥¤¦©««©§§§¦§¨§¥¤££¢¥§§§§§¦¦¦¥£¡ ¡¡¡ ŸŸ¡ž“Œvj\TSNHC>81$);AFJMPTVWYZ\]]___aeimmnnnkjmsx|€€l68@IPXaeiow|}}}~€‚‚ƒ……„„‡ŠŠŠ‹‹ŒŒŒŒŒ‹ˆ…„„ƒ‚ƒ„‡Š‡ƒrZ!#$# !"!!-Bg|‹‹Š‰‰‹ŒŒŽ‘‘Ž““‘”——––•”•—————––———–•“’“••–—˜™š˜–•—™›››””–˜———‘’——•”””œ¢ ›š•”› ¢¡œžžžžœŸ¡¡ £¥¤£££¤¤££¦¥¡£§¦§¦¢¡£¥¥¤¥§¦¥¤£¢£¤¤¥¥¥¥§©¬¬¬«¨¦¥¥§«¨¦¦¤££¥¦¦¦¥¤¤¥¤¤¤£ž ¡¡¡ Ÿžž¡¤–‰teZTRLHD@8.""29@EJPTVVX[]]]^_^`cgjjlmmmnqvy|€…€`B8CLR[chlsy|}~~€‚ƒ„ƒƒ…‡‡„„‰‹ŠŠ‹‹‹ŠŠŒŒŠˆ…„„ƒƒ„…‡ŠŒŒŠ†v\ ! !.Elƒ†ˆˆˆ‰ŒŽŒŒŒŒ“”’“”•™šš—••––——–”““““””••”“”–––—˜™š—•”•–˜™™—”•–—˜—•‘“”””•—˜›œš•”•—˜ŸŸ› ž ¤¢¡¡ ŸžŸ ¡¡ ¤¨¦¥£¢¢£¤©«¥¡¡¢£¥£ £¤¤¢£¥¥¥¤¤£¥©©§§¦¥¥§¦¤££¤¤¤¥¦¥ ¥¦¥¤£¤¤¥¤£¢¢¢¢£¥©§¢¡¡¢¡ Ÿž¡–‡qeXROJFC@7+*4>GNSVVVX\]\[\\\]`ceegikmpsx{~€oV=6DMS\dimrxzzz{}€‚ƒ…ˆˆ„„ˆ‰‡‰ŠŠŠ‰‰‹Œ‰†…„ƒ‚„…†‰‹Œ‹„^ .Fq‰Šˆ‰ŠŒŒŒŠ‹Œ’’“””–™›š˜–•–—˜–”’”””•—˜——˜™˜•”•–––˜˜–•”–™œŸ‘‘“•–œ›š™™˜™™™žœ› ¡ ¡£¤¢¡¢¢¡Ÿ ¡£¢¡¡¤¤¥¦¥¥¤£¤§¦¥£¡ ¡££Ÿ¡¢£¢¢£¥¦§©©ª«¬«¨¨§§¦¦¤££££¤¦§¦¤¥§§¤¤££¤¤¤£¢¡¡¢¦¨¥¢¡¢¢¢¢ žžŸ¡€lbVOLHD?<5*&4ALSVXWX[]][YZZ[]_abacfjnrvz~€gN95DNU^eknrxyxxz||}……†ˆ‰…†‰Š‰‰‰‰ˆ‰‰‹‹ˆ‡†ƒƒ„…†‰ŒŒ…‚a-Djƒ‰‹ŠŠ‘‘ŒŒ‘”””•–˜˜–•“”––”ŽŽŽŽŽŽ’“”•–——•–—–””—˜˜˜™˜•””—›œœ’—Ÿžœ››œ¡ œœžžžŸ ¡¢¢¢¢¢¢¤¥§¥£¤¥£Ÿ¡¢¢¡£¥¦¦£¢£¤¥¤¢Ÿ¡¤¥¤££££¤¥¦§¨©ª«¬«©¥¥¥¥¥¥¦¦¥¤£¥¦§§§¦¥¥¤£¢¢¢¢¢¤£¢¡¡¡¢¡ ¡ ¡£‹jbUKIGA;83)!6GPTXYYXY[[[ZXYY[^_``_aejosv{~‚`L87GPX`glotwwwxy{{~…††ˆˆ††‡‰‰‰‡‡‡ˆŠ‹ŒŒŠŠˆ„ƒƒƒ„ˆŒ…~l-Ag}‹ŒŒ‘‘‘’‘Ž‘””““”“”’‘‘’”•”ŒŽ’’‘’”••––””•••–—˜˜˜˜—”“•˜›œš˜¡ŸžžžŸ žŸ¡¡ ¢¤¤¢¡¡¢¤¤¥¢¢¤¤£ ¢£¢ ¡¢¥¥£¢¢£££¢ŸŸ£¤¤¤¥¦¦¦§¦¦§ª©©ª¨¦¥¤¤££¤¦§§¥¥§§¦§§¥¤¤££¢¢¢¡¡£¤£¢¢¢¡¡¢££¢ ¡¡¢¤¦šŠ‚m`SFFF@:61'.GTZ\\\[ZZZXXYXXZ\_`aa_aflquz~€‚~[I;<KT\bhmqvyzz|}}~ƒ„†‡ˆ‰ˆ‡‡‡ˆ‡†‡‡ˆŠ‹‹ŒŒ‹‹ˆ„‚‚ƒ†ŠŽŒ†s -Hf~ŒŠ‘‘‘“””’‘ŽŽŽ’”•’“””“““‘‘’”•–•““’•–——•”••”“”–šœœœœ››ŸŸœŸžžžž›œŸ¡¡ Ÿ ¢¢¡ ¡¡¢£¢ŸŸ¢¤£¡¢£¡ £¢¢¢¢¢¢¢¡ Ÿ¡¢¢£¦§§§¦¤¢¤¦¦¦¥¦§§¦¦¥¤¥§¨¨§§§§¦¦¦¡¤¥§¥¤£¢¡¢£¤¤¤¤¤¢¡¤¥¥£¡ ¡¢¢¥§œˆzh]RHGFB=81%*CRX[\ZZZ[[ZXWVUVY\]___^`fmqv|€€zVG=@MV^bhnrw{{}‚‚‚„††ˆˆˆ‰ˆ‡…„„„„…†ˆŠŠŠŠ‹‹‹ˆƒƒ†ŠŠ€{z /Ig€‘ŒŽ‘’‘‘‘‘’“••“‘Ž‘“”‘Ž’•–”’”•”’’”–——“‘Ž’˜˜–”““•’’”™›œ›››››œžœœžŸŸ›››œ ¢ žŸ ¡¡¡¡¢¡Ÿž¢¤¤¢¡¡ Ÿ ¢¤£¢££¦¤¡¢ Ÿ ¢¡¡¤¨¨§££¢£¥¤¥§©©¨¨©§¥¥¦¨¨§§©©¨¨¨©¨§§¦¥¤££¤¤¥¦¤¤¤£¢¤¥¤£££¢¢¢¡Ÿ™ƒrbXPKIEB@;1#(@PY[[YWXZ[[YXWTSTWYZ[\\]_fmqw~€}tQD=@LV]djptz}~…†„ƒ†ˆ‰Š‹‰ˆ‡„€‚‚ƒ…‡ŠŠŠŠ‹ŒŒ‰…‚‚…ˆŠ‹‰‚~|!0Jh“Œ‘“‘‘‘Ž‘“•”“‘Ž“’’““‘’•––“‘“••’‘’“–˜˜–•™››››œœœœœœœ›š™š›››››››œ›Ÿ žœœ››œ ¢¢žž£ª¨¤¤¡¢¡ŸŸ£££¡ Ÿ ¤¥¤£¤¦§¦£¢¡¡¢££¢£§©¦ £¤¥§¨©¨¦¦©¨¦¤¤¦§§¦¨©¨¨¬¯ª¨¦£¤¥¥¤££¤¥£¡¢£¥¤£¢¢¤¤¢ŸŸ¡™’r_TONKFBA>1 9KTZ[ZYXXYZXVWXVSRTVUUWY]`dhmu|€zpL@;@KT\cjpuz|}€ƒ„ƒ‚„‡‰‹‹Š‡…ƒ€~~~‚„‡ŠŠ‹‹Œ‹‰†„ƒ‡ŒŒ‹ˆ}}†!0El”Ž‘‘ŽŒŽŽ‘”•”“’’“˜—•”“’’’”––”““””’‘•˜™™šœœ››››››œ›™™šœœš™˜™šœœž ¡ ¡ žœ ¤ªª§££©°±©£žœšŸ¡¡ ŸžŸŸ£¥££¥¦¥¤¢¡¡¡¢£¤¢¢¦©¦˜Ÿ¥¥¦§¨¨§¤¥¥¨©§¥¤¥¦§¦¦§§£¨¬¬©£¤¥¦¤¤¤¤£¢ £§¦¤£¢¤¦¢žŸ§›’„taRONJECA;/+BOVZZYYZYXXWWWWWSQRTSSUX]bdejqw||lG;8AMU\cjouz{|}‚‚‚„†ˆŠ‰‡…ƒ€~}|}€ƒ‡Š‹ŒŒ‹‹‰‡††ŠŽŽ‰€|~Œ+!/Eq†–‘‘‘ŽŽŒ‹ŒŽ”–•”’“•–—–•”’’’’”•••”””“‘‘’•—˜™™™š››šššššššš™™™™››™˜˜š›œŸŸžŸ ¢¢£°¦¡ ›‘Š’ž Ÿ¢£ œžŸŸžŸ¡£¢£¥¤¢¢¢£££¤¤¥£¢¦©©¤§§¦¥¦¨¨¦¦¥¤¦©¨¦¥¦¦¥¦¥£¢Ÿ£§¥ ž¡¢¤¥¤¥¦¤¢¢ ¢¥¥¥¤¢£¥£ŸŸ¦š˜†paUQOKHE?8,0BKRXZYXXYYXWWXWWXURRSRRUX\acdflrw|{veH67BPX]cimsxyz{‚‚„†‡‡‡†„‚€~||{{}€†Š‹‹Œ‹Š‰‡†ˆŒ‰€|€Ž;#.Cr†”’‘‘ŽŒ‹‹‹‘“••”““““”•”’‘‘‘‘’”••””•””•–•——–•””–——™šš™™˜—–˜™™›œœ›››šššœžœ ª±®§œŽ‰‰†€rlrx}…’ž¢¡ ¡Ÿ ŸžŸ¢£££££££££¥§¥¦¦¥¤¢¤§ªª¨¨¦£¦§¨¨¨¦¤¦§§¦¥¦¥¤¥¤£¢£¦¦£Ÿ ¡£¤¤¦¦¤¢¢¢¢£¤¤¤£¢¤¥£¡ ¡––zf`VQNJHF>6)+DQUWZZXWWYZYXWYYZ[WSQPOPTX\_cegkpsvxl\B49ERX\binrwy|~ƒ„†‡†„„ƒƒƒ}ywxy|‡‹‹‹‹‹ŠŠˆ‡ŠŽŒ‹€~‚I##.CpŽ‘ŒŒ‘ŒŽ’“””“’‘’’‘‘“”••–––˜™™™˜•”“““”–™››š™˜˜—˜˜˜™œžŸœ››œžžœ™¢¨¤•ƒo_WOOSZV@=CQc}’œž¥¨¤§¤¤¤¥¥¥£¢¤¦¥¤¦¨¨¨¦¤££¥§§¦¦§©§§§§§§¦¤¥¥¥¤¤¥¦¥¤£¤¥¦§§¥¢¡¢¢£££¤¤£¡¢££¤¤££¢¢¤£¡ £¦’…hh`VQMJIG@6')>NWY[[ZXWX[\\ZYZZ[[XTRONOSWZ]adglqrpkaP:3<HSZ]aiosvy}€‚…†††„‚‚„…ƒ}vssux|}€‡‹‹ŠŠ‰‰‰ˆˆŒŽ‹‡€~„—\1#/GczŠŽŽŽ‘‘ŒŽ’“”••”’‘‘ŽŒŽ’•–————™šš˜––•”””•˜›››šš™™˜—–˜›œžžžŸŸžžž¢¥¢¢ “{eVV^XSQPOLKLE=@j†’Ÿ¨©ª¦¥¥¥¦¥£¢¥©§§§§¨¨¦¤£¤¦¦£ §ª«ª¨§¦§§§¦¥¥¥¥¦¨¨¥¤¥¦§¦¥¥££¤¤¤££¢£¤¢££££¤£¢¢¢¢¡ ŸŸ¥wbi_UOLIHGB5%"8JTZ\]]ZYXZ]^]\ZZZZZXWUROPSWY[^bfjmmiaUB21=IU]_dkqtvz~€‚„…„‚€~~…†‚zroprvz}€†Š‹ŠŠ‰ˆ‰Š‹’“‹……“qA"0F\q‚ˆŒŒ’““’‘“”••”’’’’‘ŒŠŠŠ•—˜˜—•”™š––˜˜–••–˜šœœœœ›š˜—–•œ›š›œŸ¢¢Ÿ¨¤–ˆwYJ[^YSMHFEC@=<?CBAVm‡›ž ¡££¤¥£¢¢¡¤¦¨¦¥¦§§§¦¨¨§¥¢Ÿ§ª«¬«©©©©©¨¦¥¦§¨ª©¨¦¦§¦¥¤¤¤¤¤¥¥¤£¢£¥¦¥¤¢¡¢¢¢¢¢¢¡ œ¡”‚hf]TOLJGE@3$!2ERXYZ\]\ZYY[\\\[ZZ[[[ZYUQPRVXYZ]begd_UD4-3AMV]`ciorux}€€€€~ywx}ƒ„vnlmpty|€…‰ŠŠŠˆ‡ˆ‹’’‘ˆƒ€†‰ƒ_"0Hat€†ŒŠŠ‹Œ’””“‘’“•••”‘Ž‘“””’Š…†‹‘”––——•”–˜“”—˜—•”•˜™››š™™™™ž•—š›œ›šœ¨°¨—‚kQQURRQLE@>=:63368:8B:kœ¢Ÿ¢£££¢¢£££¥¦¦¤¥§©©©©©¨§¤¤¦¨ª¬¬¬¬¬ª©©¨§§§§¨¨§§§§§¦¥¤¤¥¦¦¥¤£¢¤¦¦¥££¢¢¢¢£££ Ÿœ ˜jaXROLJFB<1"0EPXZYY[]][ZZZ\^_^\\^_^^\YUQPSUVWY]_\VOD2')6GQX]_cinruy}~}||{wtv|‚ƒ~ukikoty|€„‡‰‰ˆˆ‡ˆŒŽ‘…ƒ‡ˆ˜{"/Ei}†‹ŒŒŠ‰‰‹Ž’””“’‘‘’““””“‘’”••“‘Ž‰•••””•––•——””–™˜•••—˜šœœ›››œžž˜—œ›–˜¥ ŠpWZVOKJIJJD=:950,-///2:0Ch‰¥¨ª£¢¡¡£¥¥¥¥¦¥¥¥¦§¨©¨¨¨§¦¦§§¦ª¬¬«ª¨¨§§§¦¥¥¥¥¦¦§§§§¦¥¥¥¦¦¥¤¤¤§¨¨¥£££¢¢£¤£Ÿžœž‘„a_TOOLJF@8/ '@NTYZZYZ[ZYZZZ[^`_]_aba`_]XSOPSTTUVSMF>3&$-=MUX\^bhnqtw{|{zzyywy}„„uifintx|‚…†‡‡ˆ‰‰Œƒ€ƒ†ˆŒ Ž "/Cs…’”Œ‰ˆ‰‹Ž‘’’’’’‘‘Ž’“’‘‘’”••”””–œ˜—”’‘“”••—˜–”™šš™——–—˜š›œžžžžžœœœœž˜“™—‰oLSPHFFFFFFA:652+''('%',-;RvŸ©¬¯££££¥¥¥¥¥¦¥¥¥¦§§¦¦§§§§¦¥¦§©«¨¦¥§§§§§¥¥¤¥¦§¨¨¨¨§¦¥¥¥¥¥¥¥¦¨©¨¦£¢£££££¢ŸœžžŸž|a`UNKHGE?6-#:OY[[\\[ZXXY[[Z[]_`_`bcba`_\VQPQQQPME>5,% #3DPWZ]_cimqsvyzyxzyy{‚…‡…vgdhmty{~ƒ…‡ˆ‰Š‹ŒŽ„ƒ‡‰‹š•1 ".Cvˆ”ŽŒ‰ˆˆŠ‘‘’’ŠŒ’“““’“•••”•—›™—“‘‘’““”–˜—˜šœ›šš›˜–—˜šœžžŸŸš›œœœ¤¢ ™bMMJB?@BDFFD>842/("""! "'+5Lƒ‘•¤ª¦¥¤¤¤£¤¥¥¦¤¤¥§§§§§§¨§¦¥¥¦§¨§¥¦¨©©©¨©§¦¥¦¨ª©©©¨§¤¤¤¤¥¥¥¦¦§¦¤¢¡¢£¤£¢¡ŸŸ¡¤£¡uhaYQLGDC?7,4HV[\[Z[[YVVY[\[[\^``_`bbbba^ZURPMKHB:3+$'9HQX\_adhloqsx{zz|}}‚„†ˆ…ufaflswz‚‚„‡ˆ‰Š‹‹…ƒ…‡ŠŒ––]2!-@p€™Ž‹ŠŠ‰‘ŽŽ‘’‘Š‹’”••“’••“’’”˜™”’‘‘‘‘’“—šš›œœš™š›š—–—žŸžž Ÿœ”—šœ›™›–†gQIB:89<?BFGC=73/,&!$*7u‰Ž›«¨§¦¥££¤¥¥¦¤¤¥§§§§§§¨¨¨¦¦¥¦¨¨¨¨©ªª©ª©©§¦¦¨©©ªª©¨¥¢£¦¦¥¤¥¦¦¥¤¢¢¢££¢¡¡ ¡£¤Ÿœ‹je`YRNJFCA9,2GPVZZZZZ\[XXZ\]]\]_abaabdddb_[VQLEA>8/)#"0BMTZ^adhjlnqty~€€€‚„†‡‰†€wgbhntx{‚†ˆ‰Š‹‘‰…†‡‰ŒŽ’c+>m}•‘ŒŽŽŽŽ‘‘Ž‘‘Ž“”””’’•”‘Ž“”“’’’‘’““˜›œ›™—™œ–•— Ÿžœœžžš——™šœž“~dPE>3028=@BEEB=82-)$ #[€•¦¶ª§¦¥¤¤¤¤¥¦¥¤¥¥¤¢¥¥§©ªª¨§¦¦¨©©¨¨¨¨§§©©ª§¦§¨©«««ªªª¨¨¨¥£¥§¨§¥¤££¤¤¢¡ ¡£¤“–‰ne_WQOMJEB:*'=KQSTUWYZ[ZZZ[\]]]]^accbcccb`]ZUOE=950("(9FOV[^bfjlmmpuz„„‚‚ƒ…†ˆ†€whdjoswz~„ˆ‰‰ŠŒ‘Œ‡…†ˆ‹ŽŽ—¤,Ai}ŠŒŠŒ‘Œ‹‹‹Ž‘’’’‘’““ŽŒŒ’“”–”“““”—šš›œš—–––•”•˜œžœššœ›™˜š›™¦£Œ{N563+))-3;?ACBA>80*&!9l–©³ª¥¥¤¤¤¤¤¤¥¥¦¦¤¢¢¤¥¦©««ª¨§§¨¨§¦¦§§¨©ªªª©¨§§«¬¬«ªªª¨§§¥¤¥§¨¦¥¤££¥¥££¡ ¡¢£–˜‹td^WQNLHB@8&3GORRSSVXZZXXZ[\]^^_^_cdcba`_]XVSL@60*#"2BLRX]`chkmoorv}ƒ…ƒ€€‚‚ƒ…†ˆ†tigmpsx{}~€ƒ‡‰ŠŠŒ‘‹ˆ†‡ŠŽŸ– !!! -Cj|†ˆ‹Œ‹‹‹Ž‘“”“’‘‘“””’Ž“•——–”““”–—————–•““““–™š›™˜š›™—™œ«¤—|fL:1'##&)-48;>>>=80)$ I‘¤©¥£¤¤¥¤¤££¤¦¦¦£££¥¥§©©¨¨§¦¨©©¨§¦¦§©««ª©¨¨§¦¦ª§§¨¨§¦¥¤¤¥§§¥¤£¡¡£¥¤£¡ ¡¢¤œ—Šv_[WRLIE@=3!";KQSSSTVXZYVVY[[\^_^]]aca_^_^YTQME:1*"!/@KQW[]_behjnquy~‚‚€‚ƒ„†‡‰‡qhflorvz|}~€‚…‰ŠŠ‹ŽŽŠ‰‡‡‰ŒŽŽŽŽ™™H)#$%%$" .Cl€€„‡‹Œ‹ŽŒŒ•••““’“••˜•‘‘’”•—˜—”““”–—˜˜—––•””›œ›™˜™š—–™œ —„Q>:0'!#&),/158;;72,'"*„§¦£¥¥¦¦££¤¥¦§¦¤¥§¨§¨¨¨¨¨§§©ªª©¨§¦¦§©ª«¨§§§¥¥¥£¢¥¦¦¦¦¥¤¥¥¦¦¥¤££ ¢¥¥¢ ¡¡¢›u]VTRMHEA<0-DNPSUTSUYZXVWZ[[]^^^\\_a_]]][UPJC:2+$*>LTY\^^^adgjnsx}‚‚‚‚ƒƒ‡‰Š‹€qgejorvy||~€ƒ†‰‹Š‹Ž‹Š‰‰ŠŒœ›a7 $&(*)% #1Cq‚‡ŽŽŽŒŽ‘Ž“““’’’‘”••–—˜•”•”’“•”““’’”—˜˜˜˜˜˜˜—šœœš™™™˜•–™œˆ‚‡}M71'" "#%&).4774.(#yœ ¤¦§§§¦§¨¨¨¨¦¤¦ª¨¨¨¨©©©¨¨©ªª©©¨§§§§¨©¨§¦¦¦¦§¦£¤¥¤¥¥¤¥¦§¦¥¤£¤¥ª©©¦¢¡¢¢¡¢ŸxscUTTOHDA</ 4FLLPSSRTXYXWVXYZ\\\]\\]^]\ZXTPLF;3.( *:JTZ]`_^^adfimrx~€€‚ƒƒƒƒ„‡‰Š‹Œ}neciosvxz|~„‡‰Š‹‘‹Š‰‰Š‘–˜qQ&&')*'# ! '6An€˜‘‘‘’‘ŽŒ‹‹‰‰‹‘’“‘”–—–••“‘‘“‘’“’‘“–—–—˜™™˜—˜˜˜˜˜˜™›š—•• ŸˆˆrN9-% '0453,&!b˜¢®¦§§¦¦¨©©¨§¥£¥§§§§¦§¨©ªªª©©ªªªª©¨§§¨ª©§§§©ª¬§¤¤£¤££¤¥¨¤£££¤§«¬«¨¤¢¢¢¢¤¡”|scVUUNFA>7+)<HKKNQRRSWYXXXXZ[[Z[\\\\[ZYTOLHE@5,'",>JT\`ab`^^acgkpuy}€‚‚‚ƒ„„†ˆŠ‹vlcciptwxz}€ƒ†‡ˆ‹‘’‘‹‰‰‰‹Ž‘‘’‘™l*&#$$$##$$"!!!!!""""""! "$#! "&157n…–”’’‘‘Ž‘’‘ŠŠ‰‰‰ŠŽ‘‘‘‘‘•–––•“‘“’’“”•–˜˜––˜™˜——–––••–™››˜•”£¡‡wX:-!&2552*$ Hn”¤²©§¥¤¤¦¨©©¨¤¢£¥¥¥¥¤¥§¨¨¨§§§©¬¬«©¨¨©ª©©©ªªªª«¨§§¦¤£¢££¢£££¥¦ª¯ª¨¥£¡žœ˜œ˜}ncZWTME@92& ,9EKKKNQTTTVXWWWX[\[[ZZ[[ZWVSNHD@=8/'!+=KS[`bba_]]_bfkpuy{{|~‚„……†ˆŠtjbbiosuwz}ƒ…‡ˆŠŒŽ‘††‰‹ŒŽ‘‘‘˜¦€,&"%&'(()))*++***)'&()))*++++,/.:8K€Š—“’‘‘Ž’“’‹‹Œ‹ŠŠ’’’’’‘”•••“‘‘“”“”—œ›š˜——˜˜——˜––••–—™›š™˜žœU?7*(4751)$ 8X©®¦¥¤¤¦¨©ªª§£¢¤¥¤¥¦§§¦¦¥¦¦¦ª®¬«ª©¨¨¨§©««ª¨§©¨¨¨¨¦¤£££¤¤¤¥¥¤£§§£££¢ž———™zlc\YTNGC;0!'5BJNMLMOSVUUWWVWVVY[ZYWWWWVTROIC?:4-&!.=JS[_cdca^]\^bfjouz{zxz}ƒ…………‡‰ˆribahnqtw{ƒ„…ˆ‰Š‹ŽŽ…‡‹ŽŽŽŽ¤‹-&"',0234567665432267644578;?AGGPs‡Š˜“’ŽŽ‘’‹ŒŽŽŒŒ’““““‘ŽŽ’“““’’“”••”—šœ›™˜˜™™˜™™—––—˜™šš˜™«ŸN4,$.7960)$:RŠ” ¦¥¤¥¦§¨©©©¢¡¤§¨©©§¥¤¤¤¤¦¨ª««©¨¨¨§¦§¦¦©ª¨¥¤£¤¦§§§¦¥¥¦¦¥¤¥¦£¡¢£¢¡¢¢¡Ÿ›™“wlc\ZVOHC;-$)/4=EKOQPOOQTVUUWWVWVTVXXVTRSTSQOJD?:3,$$0=HQY^accb`^]]^bdfksz|xvy}€„†…„„†ˆ‡pgbafmprw|ƒ„†ˆ‰‰‹Žˆˆ‹ŽŒœ‹-%%09<=>?BDECA@ABCCB@?ACDFIMU]dw‹ŽŠ˜”ŽŽŽŒŒŒŒŽ‘’“””’‘‘‘‘’”••–—˜š›œš˜™™™š›š——–—šš˜—–“£—xV-'!"*4;<81)$+M‹”£°¤¤¥¥¥¦¦§©¥ž £¨«ªª©¢£¥¦§¨©ª©©§§§§¦¦§¦¦§¨§¥¤££¤§¨©¨¦¦¦¦¤£¤¥¤¢ž¢¢¢¢¢¡£¦›xnbYYVND?6'&-7?CIOPQRSSRRUVVVWWWXVTUXVTQPRQOLHD?91)# )4AMTZ_bddcba_^]_aceiqxyxwz|„†…„„…‡‡neaaekorw|„†‰Š‹ŒŽ‘‹ŠŒŒŽŽŽŽ›’-$"3?FJLLLNPNMORSSQNKKMOPQV\i…“ £™š—ŒŽ‘‘ŽŒŽŒ‘‘’“”““““’‘‘‘‘’”–›ššššš™—˜˜—˜›™———˜š™—•••Œz[C.! '0:BC>5*"?ŽŸ¦© ¤¦¦¦¦¦¦§¡ ¡¥§ª«©¡£¦¨¨§§§¨§¦¦§§¥¦§¨©©©©§¥£¤¥¨©¨§¦¥¥¦¥¤¤¥¦¥¤¥¥¤¢¢¡¤¥š’zm_UUTK@90!'/6>FKOSQOPRSQRTVWWVVWWVTSUTRPPQNGDA=70(" %,6CNUY]`bcdddb`^^_acdhntuwxz|„…„ƒ„…†…ld``dinrvz|ƒ‡‰‹Œ‘‘‹ŠŒŒŽŽŽŒ˜”0%2AKRTUUUUTTXZXYWTQQTWZ_fm|•š§´²£›—ŒŒ’‘Œ‹ŠŒ’’‘‘’’’’“••“’’“’‘’”›œš˜˜˜—––•’“™˜•—˜™›™—— ž€X&&!#,9EMNF9) Š¢ ž¤¦¦§§¦¥£¢¢¡¤¥§©«©¤£¦¨¨§¦§¨§¥¥¦¦¦§©ª«¬¬¬§¥¤£§©¨¦¦§§¦¥¥¤¤¦§¦¦§¦¥£¢¢¤ž˜ph[QQOH>6+!.6=ELORSQNMPQQRTUWWVUUUTTSRQONMKE?;83-&! !#)3=GQX[]`bcbcdc`^]]_bdfhjnquvy}…†„ƒ„……ƒkc``chnsvz|ƒˆŠ‹ŒŒ‘‘ŽŠ‰Š‹Œ‹‹ŒŽŽŒ—3&/@KPRUXYYWY\\[ZXUSTWZ^bioxŒ—±¥£™˜ŒŒ‘“‘ŽŒ‰‰Œ‘“”’‘“””““”””“‘‘•››˜————•””“‘˜–”–˜šžŸ“a9()/G\fdW@*#}“˜š›£¥¥¦§¦¤£¤¥¨ª¨©ªª¨¥¥§¨¦¥¥§©§¡Ÿ£¥¦¨ªªª«¬¬§¦¦§¨©§¥¥¨©¨¦¥¥¦§§§¦¦¦¤£¢¢ ˜–gbYPOMH@6(+6<BHLNOONMMOPPRSTVXWVTRRSQONLJGC>841,%! "(/5@LSX[[]bddbab`_^]]`begghknpsw}……„ƒ„……‚jb`bdinsvy}€‚†ˆŠŠ‹ŽŽŠˆ‰‹ŒŒ‹‹Œ˜Œ2%-?KPRUX[[Z\^^^\ZYZZ[]aekpz•¥«¡££Œ‹’’ŽŠ‰ŒŽŽŽ“”‘‘’“““’’“““’“–š›™˜—˜—•”””•›˜“’•—› ¡˜ˆwC&#/4Q{‹ƒ`D+!s‹¡¨ £¤¤¤§¥¤¦¨¨¨¨¨©ª¨¦¦¦§¨¦¤¥§¨§¢¢¡¥§©ªª©©ª©¨¨ª¨©©¨¦¥§¨¨§§§§¨¨§¦¦¦¥£££¤—”‘nfZQMHD>4$)7?DGJLMLLMNNOPQSTTVWXWTQQQNKHFEA=81,)&"!'2=DLTZ]^^`dffecba`_^_aceghhijlqw|„…………††ƒg``cejosvy~‚ƒ…‰‹ŒŽ‹‰Š‹Œ‹‹‹‹Ž˜Š* +>LSUVX\^^`a```^]^_^]afjoz“¤ Ÿ§Ÿ‰‹ŒŒ‹ŒŽ‘ŽŽ““‘’““’’’“••“‘ŽŽ’—˜™ššš™˜–••–™žœ””••——œ™uS."'Ca’¡¢•b@,1p³¡££¢¢£¤¥«ª¨§§§§§£¤¥¥¥¦§¥¤¤¦§¢¢£§¨©©ª©¨¨¨§¨©©¨§§¦¥¥¥¥¦§¨¨¨¨§¦¥¥¥¤¤£©œ’‹lh^WOG@9/! )3;@EHKNNLKMOOOQRSSSTUVUSPOOLGCBA=82,(#"#'/9DJOTZ^``_`cdefedcccaabdeefhiiknsz~ƒ………†ˆ„f``beinrux}ƒ†‰Œ‹‰‰‹ŒŠŠ‹‹‹ŒŒš…#+?PWWVX\^^acbbcb`aba`cfkqzŠ’˜Ÿ™˜¡žŠ‹Š‹ŒŒŒŒŒŒŽ‘’“’‘‘’“’’““•——•’Ž‘•——™›››™––––˜š™—–•”•“”ŽO1"#)g„ª¯£˜cI/*d‡¯²¢¢£££££¦«ª¨§§¦¦¦¤£¤¥¥¥§§¦¤¤¦¥£¥¨¨§¨©ª©©©¨¨¨©¨¦¦¦¦¦¥¥¥¦§¨¨¨¦¥¥¤¤¤¤£¨ž…ig_ZSI>5)#/9AEEGJMOONMNNNOQQPQQRSUTQNNNKEAA>81+&$!#-6>GNQUZ^abbcddddeeddeecbcfgfegijklpw}€‚„†††‡…hb`bejmqvz~‚…‡‰‹ŽŠ‡…‡‹ŒŠŠ‰ŠŒŽœ‚-ARXWWY^``bcccedabddcdgmt{ˆ‘Ž’”—›•‹‹‰‰Š‹Ž’”•–—’Ž’‘’‘‘’“”•––••”‘’”••—™››š™™—••˜˜–•”“žz/&2PrŠµ³›[>/ $Z‚®±¢¡¤¨¨¦¤¥§§¦¦§§§¦¦¦§§¦¦¦¨§¥£¤¥¤¦¨§¦§©©©©©¨§¦¨¨¦¥¥¦§¨§¦¦¦¦§§¥£¤££¤¤££Ÿ”ˆnf^YSI>3%!&,6?FHHHKMNNNONLLNPPNNOQSUTPLKKID@>:3+&"!$0?HJMRVX\`bbddddddeeccddcbdghffghijjnsx}ƒ…†‡…ha_aeimrvz~‚…‡‡ŠŽŽŠ‡†ˆŒ‹ˆˆŠŒŽ‘•r.CRWWY[^_abbbdfecceffhkou|„ŠŠ“‘Ž‹‹ˆˆŠŒŽ‘“”––—”ŽŒ’“’‘‘“”••”’’“”“’’““–—™™™šš™”•—˜–”’¢œ}Y"Qr‰žº· “^?/$d¨¶¢£¥¨©ª¥¤¦§§§¨§¦¦¦§¨¨¦¥¥¦¦¥¢¤§¦¨©§¤¤¦¨§§¨¨¨§¨¨¦¥¥¦¨¨¨¨§§¦¦¦¤££££¥¥£¡¢šŠdd[WQF<2"'.37=DHJJJKKLLLNNKJKMNMMNNPRQOJGEC?<94+#! #.APUSSX\]_bccefeffeffdcefedfhihhhijjjmorz„‚„ˆˆ„}i`_aflptwzƒ…‡‰Œ‘‘Ž‹‰‰Œ‹‰ˆŠŒŽ”…X-APVY\]^^`a`adfeddegijlotyƒ…‡ŠŒŽŒŠˆˆ‰Ž’‘‘‘“–•””‘‹Ž’””’’”••“’‘‘“““‘‘’”—˜———™›››ššš—“‘™ŠP; e¨¯·²ŸŒ\A-$i} ³¦¤¤¦©©¦£¥¨ª©©¨§¥¦§©¨¦¤£¤¥¥£¤¨¨©ª¨¥¥¦¨§¥¥§¨©ª©§¥¥¦§¨¨§§§¦¥¥¥¤¤¤¥§¦£ž¢œ‰caZVOC8."%-37;?DGILKIHIJJMMJHHKMLLKKMLLJF@=<:61*# !)5ANWZYY\_abcdceffggfffddfhgffghijkkkkkkklqy~ƒ‡‡‡|f]^bhmrvwz‚„‡ŠŽ‘’’‘Œ‹‹ŒŒŒŒŽ˜vE,@PVX[[ZZ[]^`deeeefhjlmpux{~€ƒ†‡Š‹Š‡…„…‡ŽŽ“••ŽŽŽ‘“”’’“”“’“””””““““–˜™˜–—™››œœœš—““š“u,(<i”¤¨§«”‚`D+!`xŸ²«¤¤¤§©§£¤¨©©©©¨¦§¨©¨¦¤£¤¥¥¥¦§¨©©©§§¨¨§¦¥¦¨©ª¨§¦¦¦¨©ª§¦©¨¦¥¦¦§§§§§¡¢£˜‰gbZTL@3)"!&,17<?BEGKNLHGHGFIKHEFJKJIHIHDCC@:652+$ #-=JOT[]^^^_bdddceghiihfeddfhhfeegjklmmmmljjlrz‚†„…cY^djnty{}‚…‰‘‘‘Ž‹‹ŒŽ‘“—d; )=MSUWXXZ[\\]_`acdefhlnqtwxz|„‰Šˆ…ƒ~†Ž““Š‹ŒŽŽ’‘’’’’‘‘’”–—–”“””–˜šš™—™›››œ›˜——¤œ‡_l†¢¨˜˜ ¢Œ‚hC(WsŸ¦¤£¤¥§¦£¤¥¦¦¦¨ª««ªªª§¦¥¦¦¦¦¥¦¨¨§§¦§¨¨¨§¦§¨¨¨¦¦¦¦¦¨ª¨¦¦¨¨¦¦¦§¨¨¨§¤Ÿ£¡Žgc[RG;,$"$).059=ACDFIKJGGFDCEGFEEEFEDDEC>=>;730*! /@LRSW\_a`_`cdddcehghjhfecdfggfeefikkllmoollkox€ƒƒxm_Z`dhmrx{}‚†‹ŽŽŽ‹Š‹ŒŒŽŒ‘œ•F*'<JPSUUWY[[\]]^`bcbcgkmqvxxyy|…ˆ‰†ƒ€€ƒ‘‹ŠŒŽŽ’’‘‘’“•–——“’“”—™šš™—šœœœœ–“•—žŒgC&ƒœ¨¡—•— Ž‚b=&\sš§œŸ¤¥¦¦¥¥£¤¥¥¦§ª«ª©©©§¥¥¥¦¦§¦§©§¤¤¥¦§¨©¨¦¦¨©¨£¤¥¦¦§§¦¥¥¦§¦¥¥¦§¨§¤ž™œžzhc\QC5& #'.36;=?CEEEEFFGGFDCDDEEB@@?==??:89742,%)?OTVWY]`bcccdeefghhhikkigefhhhggfgjkllkmpponmow~€lc\]bfjnrw|€ƒ‡Œ‹‹ŒŽŽŽ¡”"';IOTVVY[\\^_^^^abbdhjjox||ywz€„‰‰†ƒ€„““‘Ž‡…ˆ““’“•–—–“’“”–—˜˜——™›™–”’‘“˜ŸŒu.Amš¥¥ž•••šŽZ9$d|˜¡™Ÿ¢¥§¦¥¤££¤¤¥¨©ª©¨¨¨¥¤£¥¥§©¨§§¦£¢¤§§¦§§¦¥§©¨¥¤¥¦¥¥¦¦¥£¤¥¦¥¤¥¥¦§¥ ˜˜œ•|da\P=,!$*057;=?BDDC@@ABEFCBAAAA?<;9889976630.(!"0BPWYZZ[]`bdedcdefhhggiklkiggijjigghijkkklnnnonou{}{eZX\beimqu{‚‡Œ‘’’’ŽŽŽ¤˜';HOTWXZ\[\^^]\\_adgjjjoyƒ‚‚‡Š‹…~}~‚…ŽŽŽ‘‘’‘‹ƒ„‰’“•–––•“””•–•”““—–‘’‘’’— €e"yŽ£¯¥Ÿš–—“‹\>#aƒ¤¥£¢¤¨§¤¤££¢¢£¦©©¨¨§§¥¤£¦§¨©¨¦¥ Ÿ¡¤¨¨¦¦¨¨¦¦¨§¥¤¦¦¥¤§§¥¤¤¥¦¦¥¥¤£¥¦¨ž™˜~h`ZM3$!#(/378;=>@BCA><::?A?==<::;;864332111-(&"!(6EQX[\]]]_bdeeeccdfghghiklljiijjjhghjjjklmmmmnpppsuus^ST[beinru{ƒ„ˆ‘“”“ŒŒŽŽŒ¦š(:GPTWZ]^^^^]]]^_acgjjjow€…Š˜ ›‘‘„€~|}€„‡Ž‘“””’Œ‰ƒ†ŠŽŽ’•”””–—˜——–“‘‘Ž•–‘’“”•”¦šfE "=•ŸŸ¥Ÿžœ¢ž‘„Y4!T| ©¯¨¦§©§¤¢£££¢£¤¥¥¤¥¦¦¦¦¨¨ªªªª¨¤¡ ¡£¨§¥¦¨ª¨¦¦§¦¦§§¤¥§§¥¥¦§¨¨¦¤¥¤¤¥¨§›“„{laXG*"',13358;;;<<:86569;:8765677410.+)())%! "-=IPVZ[[]^_adefedccddfghikkkjiijiijhgikkklnnmjlopqqpnkcWRU\beinsw{€ƒ…ˆŒ‘’’ŒŠŠ‹ŽŒŠœƒ ):IRTVZ\]\]]\[]^^^aehijpw‹Ž›¬«˜’„{{}€„‡‹ŽŽ‘““‘Œ‹’“’‘’•™™™˜•‘‘Œ›œ“‘“–—¢‘E,)j¢© žžœ£¤“‚L/Jt ª¬§§¨¨©¡¡£¥©«¨¥¥¤£¤¥§§§§¨©©©ª§¤£¢¡¤§§¦¦§©ª¦¦§¨¨¨§¦¦§§¦¦§§¨©§¥¦¨¥¤§«¡’~wjaWC&#+13202678786433467632222320,*)'%"! ,>LRVY[\]`abdffgeddeeegijklmlkkkkjjkihilmmmnmkhkopstme_XRQV[afkpuy}„†‰‘Šˆ‰ŒŽŽŒŒp'9KSTUY[[[\[[[\]^_aefgipw‘’¦« ”„~yy|„‡ŠŽ’‘ŽŽ‘’Ž’’‘“’‘”˜™™˜–“’”– ž—™›š¢¡–|#q“¢¢›œœœœ› ¡‹t<0Mx¤®«¦§¥ ¡¡¥ª¬¬ª§¦¥¥¦¦§¨¨§§¨¨¨§¥¤¥¥©§§§§¦¦§££¥¦§§§§§§¨¨¨¨¨©©©¨§§§£¢£¥£–wg\R=#%,11000222221/00143/,+,-.,*&#"" +9GQVYZ[]_aabdeeeeeefffhjkklnnmmmlkklljjkmmllkihjoqsqeYQNMPV]chlpuz~‚„†ŠŒŽŒˆ‡‰‹ŽŒŒ’‹e&9KSSTWZ[Z[ZXY[\]_adddgnu~’–™¢¤« ’ƒ€xw|ƒ†‡ˆŠŠŒŽ’“’Ž’’““ŽŽ‘”——––•””–š›š›œ£¦›‚c,Z¡´¨Ÿ›œœœ £qG.QxŸ¨¤¤£žŸŸ£¥¨¬«¨§¦¥¥¦¥¦©©§§¦¦¦¦¦¥¦¨«¨§¦§§¦§¥¢£¤¥¥¥¦¦¦¦¨©©©ªª¨§¦¦¦£¡¢ ›—‰{gXI6# ',/01100..,+***,-.+'$$$&&# .@KRW[\\]_acbcefeedefffgikkkmppnnnonnnnmllmnlkkjiknomgYLGGJPY`ejosw{ƒ…‡‰ŒŒ‹ŒŠ†…‡‰Œ‘’Œ–}X%8HPQSVZ\\\[YZ\]]]`bbcekqyš Ÿ ¥§—wu{ƒƒƒ„…ˆŠŒŽŽ’”•“ŽŒŽ‘‘’Ž–˜—–••””••™ššš ››Z7d…«µ£žœœœžž¢¥sG-"Ws˜ª£££££ªª¦¥¦©¨¦¦¦¥¤¤¤¥¨¨§¦¥¥¦§¨©ªª©¨¦¥¦¥¤¥¦¤¤¥¥¥¥¥¥¥¥¦©©©ªª§¤¤¥¦¥¤¤Ž†vfWC0#!'*+-00-,,*($"""#$$!,?MUY[]]^__acccdeeeceeefgjkjjmppnlmoooooonmnnnmmlllkhaTGACFKQZ`dinrux|„„†‰‰‰ŠŒ‹ˆ…„†Š““’‘•uM%7FMPRUYZ[\[YYY[[Z\^_achotŸª¢ ¤¥˜ƒ€rsx|€‚„†ŠŽŽ‘“”“‘ŽŽŽŽŽ–š˜–––•”““•™šš˜’’…8•¥¯ª¥ œ››¦£ˆd?**d{ ²¨¤¥¦¦©©¨¥¤¥¦¥¦§¦¥¥¥¦§§¦¦¥¦¦¥¨ªª©§¦¦¥¥¤££¥¦¥¦¦¦¦¦§§¦¥¦§§¨§¥¡£¥¥¥¦§’ŒzpgXB,!!'*+,,+)(&%# ,=IRZ^^]^`bacggeddffeeffgjklkkmppollnoopppppponnnmmkf]N?88>FLT^cejortw{‚ƒ…‰Š‰‹‹Šˆ……ˆŒ’”“’–lI%5CMOPRSTWYYWVVYZXXXZ\]bjvŒŸ²¯£¢’}tptx{~€‚ƒˆŒŽŒ‘‘‘‘’‘ŽŽŽŒ–š—•””””““•˜š™•‡o/^“™›—•š›œœ›šª¥Q6&0r‡¦¶¬©§¨¨¨©§¥£¤¥¤¥§§¦¦¦¦¦¦¦§§¨§¥§©©§¥¤¥¥¤¤¤£¤¥¥¦§§¨§¨©©¦¤¥¦§¥£¢£¥¤¡¢¥¤œŠsog[D+!!%()(%#"" ,<GOV[]]]]adddggeccegffgfgjkllmnooomlnoopoopqqpnopmke^L<214<ENV_cfkpsux|‚„‡Š‹ŒŒ‹ˆˆŠ“”“’—œf9$0=FIJJKMQSQQSUVUSRQSUVZcu‰›¦©¬ªŸŽyysoqux{|~€‡ŒŒŠ‹‘‘‘‘Ž’•—“’“””•—ššœ‘tQg†¦«›™—‘•›žžœ™¤¤|O2$/oˆ¤²¬§§§§§¨§¥¥¥¥¤¥§¦¥¦¦¥¥¥¦§¨©§¥¦¨§¦¥¥¦¦¦¦¥¥¥¤¤¥¥¦¨§§¨©¨¦¤¥¦¥¤£££¢ ¢¨™„pmg\D* "$%$,@KPSX[]_``bdeeggeccefggffhkkkmonnoponoppponoqsroonib]D6,)-3<GPX_dimpsw|‚†ˆ‰‹ŽŽŽŽŒ‰‰‹“”““’ž£X("+38;=?ADHIIJMOMKIHGGJLPVcw‡’–šœwuropsvxy{}€‡Œ‘ŽŠ‰Š‹‹Œ‘‘’““‘’“””“‘“”””–œ £•[5Š©®ž™”‘›ž ¡¡’kR.!l‰¢«¥¦¦¦¥¦¨ªª«¨¦¦¦¦£¡£¤¤¥§§§¨©¨¦§¨¨§§¦¦§©¨¦¥¥¥¤¤¤¥§¦¥¤¦¨§££¤¥¤¤¢¢¢¡ ¡¤•†tkeZA) !!,?KOQTX\^_`abbcdedccbcdfhhgikkkmoonoponnnppnmnqtromhaK4.(%'.6?KSX^ejmorw|ƒˆ‹‹ŒŒ‹‹Š‰‰‹Œ’’“”“¢¤H $(*,.148;<=?A@><;:9:=@CHMUjy†‰Šƒwsqnnqstvxz}…‰ŒŒŠ‰‰‹Ž‘’”•”“’““”””“‘’”•”“’Ÿ¢¡”@ :–¥§ª¢˜™ ¡ ž¡›ƒ_P2#v‘¦«¥¥¥¥¤¥¬¬ª¨¨§¦¥£¢¤¤¤¦©¨¨ªª©¨©©¨§§§¦¦¨¨¥¤¥¤¢£¤¤¦¦¥¤¥¨¨¤¢£¤¦¥£¢£¢¡ ¡–ŒwkcW?) ! '5BLPRSV[___`abbbcdccdcbbeiiiklkknqqqrrqonnpqonoprpkkaL,#&1:DNU[ahmopsv{„‰ŒŒŒŠˆ…„…‡‰Š’‘’”“§£:!""#%'*,-/121/.-,-/1469?BLR_morrsqomlmoqrux|ƒˆˆŒŠŠŒ‘•–••••––•”‘‘•––•““ —„&Iož¨ª¥¢ŸŸŸ ¡ ž™Ÿ¢‹bM/#2€—µ¨§¦¦¤¢®®«ª¨§¦¥¤£¤¥¥¥§©©ª««ªªªª©¨¨§¥¤¥¥£¤¥¤£¤¥¦¦§¦¦§©¨£¡¢¤¥¤¢¡££¡ŸŸ’ˆtiaT?,! #$$#""! )4>FLPRSTX\__^_`bbbbcbcdcbadgikmljknpqqqrronoqrpoppokfO5, (2;ENU\aglpqtvy~ƒˆŠŒŽŒŠˆ„‚ƒ†ŠŽ‘“’¥Ÿ,!$$##$%$##$%'+.02468;9HYdkmnomkhimpqrvz‰‡‰ŽŒ‹ŒŒŒŒŽ–———–•••”’‘’–™™˜•“ œ‡swŒ§¦¡ŸŸ ¡¡žšž ]F*9™´»¨¨¨¨¦¥§§¨ª§¦¦¦¦¦¦¦¦¦§©ªªª«¬¬ª©ªª©¥¤¤¤££¤¥¦¦¨¨©©§§¨ª©¤ ¤¥¥¤¢¢££ šœ…slaSA2%"$&%%%#!)6@FIMPRSTVY]`_^_`abbaaabcdcdegikkkknpqppqssqqrrqooomhfO-"#-5>HPX^aekqsuvw{…ˆŒŒ‹‰†‚‚†‹Ž‘’‘£š# """! #(.35654216EYekllmlkiinqrtvz~‚„‰‹ŠŠ‹ŠŠŠŒ”–––•“’””““”–˜™™—“ —oQ!‘—’¡œŸž ¡ ¡¥‰J<%?‚¶´§¨©¨§¦¥£¡¥¥¥§¨¨¨¨¨§§¨ªª©©ª®¬©§¨ª©¥¥¥£¢£¤¦§ª¬«ª©¨§¨¨¨§¥¥¦¦¤£¢¤¦¤˜”‡uldWF6)!!#%%%&&!%1=EKNOPRSUUWY\__^`aabaaaabdeffghjjiikoqqpoopqrrrrolljgU:(39AKRX]adhnrvwwx~„ˆ‹Ž‹ŠŠˆ…ƒ„‰Ž‘’’£˜"#"$# '.5:>>;7422?Ucjlmljihinpruwy{~‰ŽŠˆ‡ˆˆˆŠ””••”‘’”••–˜™˜™™š˜œL)Ož§žžœ››œ››ŸŸ¢¨›…G4!&M…²¤¥§§¦¦¤£¢£¤¥§©©©©ªªªª«ªª©©«¬ª¦¥¦¨©¨§¥££££¥¥¦«¬«¨§¨§§§§¦¦¦¦¥££¥©°›Œ„rjcZJ7%!#%$$%#!-:CINQRQRTUVWWY[^_abbbbbbbbcefgghijjhilprqpooopqqpolgg^H++8=FOUZ_bfilpuwww}„‰‹ŒŠ‰‰ˆ†„…Œ’‘‘’’‘Žœ˜!#%%%&$$-5;BGHE@;86?Uemqqolihjmmotwxz~‚‰‘ŒŠ‡†‡ˆ‡‰‘’”–”‘‘“•––—™™—–—¡¢–$Px¥¬œšœžœ›ššž¦¨•zA-2U…œ®©¡¢¥¥¥¤¤£¢¢£¥¦¦§¨©ªªª©¨§¨ªª©©¨¦¦¦¦§©¨¥¤¥¥¤¤¤¤¢¤¦¦§¨§¦¦¦¤£¤¤¤¢£¥ª¯›Š}zlfaYL7!#""!#-8AHMPRSSSUVVWXXY[^_aaaabcdcbcdefgghiihilnppooooonmljeaI)%,9@ISX\_cgjlosuuv|…Š‹‹‹‰‰‰‰‡„…Ž‘’’““™™!$(*))**'! #*29@FJMMIA;8?Vfptsqmjiklknrtwzƒ‡‰‰ˆ†…†ˆ‰‡†ˆŽ‘”–•“”•–•”•——•“”¨¥„gl‹¨¨™˜šŸ ¡Ÿ™™œŸ«§‹l7/6Q‡ž°§œ §¦¦¥¤¤¢¡£¦¥¥§©©¨§¥£¥¦§«±¬¨§§¨¨§§¨¨§¦§§¦¥¤¤¤¤¥¦¨ª¨§§§¥££¤£¡¡¤©™Œƒyeb_YN7+8AHLOQRSUTTVWWWYY[]_`aaaabddcbcceghhiiiiklmnppooonljhh\F%$1<DNV[^cgjlnpqpqu}†ŠŠŠ‰ˆ‰Š‰‡„†Œ‘’’““‘Ž™›"(,/.--,*'%&),29>DFHMQNE=:AVfqutqolhhhiknquy}‚ƒ…†………‡‰‰‡†ˆ‰””””–—–”“”––•”“¥a=„˜¦˜—™Ÿ¡ ž›šœ¥a?+<ZŠ®¤Ÿž¥¦¦¥¤¤¢¡£¦¥¤¦©©§¤££¢§©ª ¤¦§©ªªª©§¦§¨©¨§¥¥¥¦§§§§¦¦¨¨¥¢¢£¢¡¡£©¯™Ž‰y`^^ZO5(4?GKMNPPQRTTTUUWXYYZ\___abaacccbccdfgghgghkmmnopomllie`K+# +7@IPV\`eiklnonmov~†‰ˆ‡†‡ˆˆ‡†…†Œ‘’““’šœ( $*/21/...-/0237=CFGIMQNE>;BUcotqonlgbadhknsvy~ƒ…„…‡ˆ‰ˆ†‡ˆŠ‘’“”–˜˜–•“–˜˜˜™¦™H!Q—£¢›››Ÿ¡ Ÿžœœ›ª¦€`=' Ge‰›¯¨ž¡¤¤£¢¤£¡§§¦¤¦¨ª§¥¥¥¤¨ª¨¤¢§¨¨¨ª©§¦§©©©§¥¤¦¨¨¦¥¥¥¦§©¤¡¡¢£¢Ÿž«¬–Š}a[[XM2#0:AHNPQQQQQRTTTTUWYYYY\_^_```abcccccbcdefffilmmmnonkiff[F%%/8@HQW^chlnonlllov†‡…„ƒ„…†……†ˆŒ’“”““‘œ4&%+032..//15799;?DEFIKMIA;9@Ranrommld^]cilnqtx|ƒ„…†‡†…„„†‰ŒŽ‘”——˜˜—™›šš§¢“(Bz§ª›žžžŸžžœ›§¢|_9#$MmŠ´±§¥¤¥¤¢¢£¦¦§§¥¤¥§¨§§¨©§©©§¥¤¡«©©¨§©ª¨§¨©©¨¦¤£¡¨¨¥¤§§§§¤¡Ÿ¡£¤£Ÿœœ™‹fZWUM5"*4?FHJNQSTSSSSTTSTUVWWXX[]]]]]^`bcdedddccdefgijjkkmnlhc\@%$'2:AJRW^filppmiilpw€…†ƒ‚‚‚„…†‡‰Œ“”””“’£@%+0330---/169:=BEDBDGGC<75>Q`lpmkjh`\_eilnpsw{~ƒ……ƒ~€ƒ†ŠŽŽ‘•—˜™›œ›ž¤—Šq‘¬§˜šžŸŸŸœœžž œyV1Ms“Ÿ¯¦¥¥¥¦¥££¦ª¨¨§¦¦§¨¨§©«ª¨¨¨¦¦¦§ªª©¨§¨©©¨¨¨¥¤¥¥¥§««¬ª©¨¨¨¡ž ¢£¤¥Ÿ›’‘‰}cYURM:*4;BIMNOPSTTSRQRTSSSTTTUVVY\[[\\^_aceecceeccegiihhhijiedX= *8BHOV[ahkmonkghmrz€„…„ƒ‚‚ƒ„‡Š‹ŒŽ“””””’¢›J!#*0684.+*))-28<AC@<;=?<621=Rcorpmjgb_behkmnqtw{‚…„€|{~ƒ†ˆ‹ŽŒŒŒŒŒ”–™šš™£…rŽ¡«¥–šœ¡¡¡Ÿžž¡¤˜zS*!Sz ¦¬¤££¥¥¥£¤¥§§¤¥§¨©©§¦©«ª¨¦¥¦¨©©©¨¥§¨¨¨¨¨§¦£¢¥§¨©«¬«ª¨¦§¨©¤¢£¤£¡ §¦”†€yaXVTO?&/8?DHLNPQRSSRRPOOQSRSSSTTTTW\]\[[]_`bdcbbefdcdghgfhihfc`G+!#.=HNS[_dhjkkigefkr}‚„……ƒ‚„„„‡‹ŒŽ‘••”””‘£ŸG"*18;60+($!$*18=>;75564/./<Ugqsqnkgb`bdefiloqtx}‚ƒ‚~|{}„…‡ŠŒŒŽŽŽ’”–˜˜˜« sT“¦§¢œ›šŸ¦¢Ÿžžœ¢¤›’nM&)]~¤©¤¢¢¤¤¤¤¥¥¤¤¤¤¦ªª«¨¥¥«ª¨§§¨©©©¨§¦¥¦¦§©©§¥¥¥¦¨©¨ª©¨¨§¦¦§¨¥¢¢£¢¡ ¡©–}}veXUQL?% +6<AEHKNOOPOPPQRPNNOQRRRSTTTTW\^\[[]^`bddccefdcfhhfeghd\WB"#-9EMRX^dikllkhedhnu…‡‡„€„……ˆŠŒ“–—–•”¦ž) (/562,'#!(/5630-++(%'-=Wirtqpokecdcaabgjlprw~€€~ƒ„„‡‰‹Ž”“’’’““•––¥˜X*)Xš¨Ÿžžœš•—˜šœžŸžœž ˜‰YH#0by «£¡Ÿ ¢¤¥¥¦¥¤¤¤¤¥¦§©¨¥¤§¨¨¨©ª§¦¦¦¦§¦¥¥§ª«©¥¥¦§¦¦¥¤ ¤§§§¥¥¥¤¢¡£¤£¢Ÿ›¤›…~teZVPI>+#-7@DFHKNPNLKJKMOQPOOOPQQRRRRRSVZ\[YZ[\_cffeeggfehjhdcc_Y< !+6AJPV[`gkmmmlhedhpw€‡ŠŠ†‚‚„†‡ˆŠ”•——–”’£•%*./,'$ $)+)&$" )=Wirsooomheeb^\\aefhmty}}~‚ƒ„‡‰‹Ž’•”’‘‘“—˜œŽ3T€§¬œžœ™˜™šššŸŸ¡•†WA%5cvœ©Ÿžž ¥¥¦¦¦¦§¦¤¤¥§¨§¦¦§§§§¨¨¦¥¥¥¦§¨¦¥§ª«¬£¥¦§§§¦¤£¥§§¦¥££¤£¡¢¤¤¢žœ£œ‹zqf[VOG=0#(3=DHJKMPSSOKHGIKMNMMNNOQQQQQQQQTWXXXXZ[^ceedefgfgijgdaZS:%'3@JPVZ]cjnppolgddiqx€†Š‹‡„€~ƒ‡ˆˆŠ“”–—–••¦ "&)*'# &=Ufpqnnpoljhd^ZY_backsx}~~„………†ˆŠ‹Ž“•”’’”›•‡$v˜¯«œœ››œœžž£¡’€Q8 ;mƒœ¦ŸžŸ¢¤¥¥¦ª¬©¥¤¥¦¦¦§§¨¨¦¤¦¨¨§§¦¦¦§¥¥¦¨ª®©¦¦¨§§¦¥¥¦§§¦¥££¥¤¢¢¤¥¤£§£œŽrlcXQKD<2'%.6>EJMMOQSVVQLIJKKLLKKLKLOPPOPQQQRTUWXXZ\`eecbceffghhea^E&#-:GOTY]_flprqplfdfksz„‡ˆ†„~~…‡‡‰Ž’“–˜—••¥Œ !#""%&"&=Udnqonppnkhc]XX[_`dlsx}~€…‰‰ˆŠ‹‹‹Œ“’’Ž“› €†£®¡œœœœœœœ›œ x@,#>nŠš¢¤¡¡ Ÿ¢¥¤££®¬¨§¦¥¤££¥§§§¥Ÿ£«©©¨§¦¦¥¥¦§§¨¯ª¦¨¨¦¦§§¦¥¦§§¦¥¥¦¦¥¤¦¦¦¦¦œ‘kg^SLF@:0&!+5:?DHLOQRSRSSQLIJKIIIIIIIKMNNMNOQQPQSVY\^_beecbbdgffeb\WB'&.7DOSV[_cinqrqnlhfiov{‚„„ƒ„‚€~‚†ˆ‹’”–——–”¥Š"$&%"""'>Wfptsqqqnid`[VVY]bgmty|}|}„ˆˆ‡‰Š‹Š‹Ž“““’’¤¤`)4Ž¥£œ˜š›™šžœœ›œœsE+(Bk‡•š¡¢¢¡¡¦ª¥ž— ¦¦§§¥£¢¡¢¦¥¥¤¢ ¤§§§¨¨§¦¦©¨¥¤¥¤¥¦¦¥¥§§¦¤¤¥¨¨§¦§¨¨©ª¨§§¡˜œ”daZPIB:0'! &07=@CGKNPQQQOMMLIGHHFEEGHGILLKKLLMNNNOSW[_``abccbacgfdaWC)#%.6?IQUX^bdinrtrnkiilqwz{~€‚ƒƒ‚€€ƒ‡‹’”–––•“¦‡$')'$!!"&?Zjtxwutrlf`\VRSV]dinty|{z{€„…„…ˆŒŒŽ”–•“’§¦oF;[–¥›˜——–››™”–¡ŸŸ ¡œ…h<$(Du˜£¢££¤¨ª¤—›™¡¥¦§¦¤£¢¢£¤¤¤££§©©¨©ª©©©ª¨¥¤¤¤¤¦¦¦¨©ª©¨¥¦¨©©§¨¨¨®¨¢£¢ —œ™m`ZRJB4'!")3<AEGILOPPOOONLIGFFGFECACEEGIIHILLLKLMPSVY^aaa``bbbbcb]YF-%-6>GOUY]befiosusoljkouxyxz}ƒ„„ƒ‚€‚ˆŒ‘“”•–•“¥&)-+&# !#$!%@^mvzywvsld_[UQQSU]cjqw{|{|~‚ƒ‚ƒŠŒ”™™™•‹’’©œO-YvžªŸœ™—˜œ›š¢£¡¢£¥©šw]30Q~“¡¤¡¡¢£¥§©ª¥¡£¦§§§¦¦¥¥¤££¥¦¥¦¬¯«©©©ªªª©¨§§§¦¦¦¦¥¦¨©ª©§¦¦¨©¨¨¨§¥¡¡¢£§ ›—rc]VM@." $+4<BHKKLNONMKKKMMHDDEEDCCA@ABCFFFIKLJIIKMPSVZ^__^_`aa`^XN3& %,3;ENSX\afggkpsutpnlmquxwtuy}€‚}‡Œ‘“”••“¥‰&)..+'%##!"&&"$Bfovxyxvslgc^XSRQPU^fntz}}~~€€‚‡‹“˜˜™™’‘’“¤’,!gƒž¬Ÿ¢ŸŸœœžž ¡¡ ¤¦—sW)3X}”©°¥££¤¥§©©©§¨©¨¨§¦¥¦¦¦¥¥¦§¦¦¨®¬¨§§¨¨¨¨¨¨¨¦¦§§¦¥¥§©©©¨§¦§©©¨§¥£¢¢£¤¨§’se`WJ8& %.6=AEJOONNMLKIHHIIFCBCCBAAB@??@CEEGJJGGGHJNRTW[]^^^__]YQI2 %-4:AKRV[`eiijnrtttrpooquwvsruxyz|€~}€‡Œ‘’“”“˜ '*/21.,,+($ #''""@hsuxxwurnkhc[WRNKNW`gnv{}€€€€‚‡ŠŒŽ“™—””’‘•ž‹ uŠ–™žŸžžŸ ¡¡œ¡¢oW$5[vŒ«³££¤¥¤¥¦§§¦¥¥§§¦¥¤¤¥§©««§¦£¡Ÿ£¥¥¦§§§¨©©§¥¥§§¦¥¥§¨§¦¦¦¦§©¨¦¦¥¥¥¥¥¤¥§œwh`TB.)4;?CGKOONNLJHGFEDCCBA?@@??A@??@CDCDFDBCCCFKNPRVZ]^^^ZTJ:(!$*28>FNTX]bfijlqttstsrqqrtwvrprvwwy}~‡‹Ž‘’“”“—˜u)-3678764/)#!%('"!:gpvzywtqnlib[WQKGKW_dltz|}~ƒ†ˆˆˆ”“‘’’‘•‘‚‘ •–—˜œœž ŸŸ žžŸ˜„eP#6Ws‰®¤¤¤¤¤¤¥¦¥¤££¦§¥¤¤¤¤¦ª««¤¢¢¢ £¥¦§§¦¦¨¨¨¥¥§¨¨§¤¥§§¦¥¦¦¦§¨¦¥¦¦¦¦§¦¤£¤™ˆni_M5#,8>BFKNONLKJGECB@???>>=<???@??@@BA?>><;<=>AFIKNQTVXXWRE4##'*/7>EMSW[aehkmotusrssrstuuwvsqrvyz{}€€‚‡‹’”•”“”o"+07:=@B@;5.&! $'*(# 6csx|{ywsolhaYTMFFKT[bjrxz{|}}~€ƒ…†‡‰‹‘“‘‘’•š{c']¦®¥™˜™œœš›Ÿ¢¢¡ŸŸ¡¤ŸŒuSA#:Q›µ¨¢¢¢¢£¤¦§¦¥¥¤¦¥¤¤¦¤¡ Ÿ¢¡ž¡£¤¤¦©©¨¦¥§¨§¥¥¨©¨¦£¥§¦¥¥¥¦¦¦¦¥¥¦¦¥¦¦¤¡¢§™„gi_H(-8>CGLNNKIGEDB?>=<;;::::<==;9;==>=9787668;=@BDGIJKKID6'#&+.4;BIPVZ]cgjknquvtqrsstvwwvwtrrvxz{}€€‚‡‹“””•—”‰n#).7>EIJHB;5-&$%(*($!=ar}|zwrnh_VQHCEJOU]hrwz{|}|}€‚„…†‰Œ‘““““™¥uD@t¤¯™™—˜›œ™™Ÿ¡£ žŸ ¥ž†nP2#?Cˆ£¬¦£¢¡¡¢¤¦§¥¥¦§§¦£¥¦¤¡ Ÿ¤£ ž ¦¦¦§¨©¨¦¤¦¨§¥¥§¨¨§¥¥¦¦¥¥¦§¨§¦¤¤¥¥¤¦¦£ ¡«œ‹pl`F!!-6?EILNKHFCA@?<::98766779:855789:953554359:;<=>?>:60&$),05<BHNTY]`dhjlnruwurruvvwxxxwurrtwy{}~‡‹Ž‘“”””—•Šl!'.8CNRPLF?92+'')*($!>bsƒƒ€|wsojbVOHDEHJNXfqvz|}}|~€‚ƒ„‡Š‘““““ª§o3b†›¦˜—•–š›š›žŸŸžœ›š¡˜€oL,F]’ œ£££¢¡¢£¤¤££¥§¨©££¦¢ ¡¤¨©¦£¥¦¦¦¦§¨§¥¤¦§§¦¥¥¦¨§¥¦§§§¦§¨¨§¦¤££¤¥¦¦£¡¢£˜‘~o_C$/6>EGIIE@?=::97799874322476346788842343235544320+$$*/15;AFMSX[^adgjlpruxwtuwxxwxxyyvrppruy{yz|‡‹Ž“”““’–…g)/:FU\XQIB<60+*+,*% >at‡…†ƒ}vspkdYPJEEEEJUcmsx{{|}‚…ˆŒ‘‘§ Nœž˜˜–”š››œžŸ›››˜•›’|oF+ Nsž¥ž¡¢£¢¢¢¢¢¢¡¢¥§¨°¡£¡ ¢¤¦¦¥¤¥¦¥¤¦¨©§¤¤¥¥¤¥¥¤¥¦¦¥¦§©©¨§¨§¦¥¥£¢£¦¥£££ –““„p]?%09@EFEC>987564258::9521025425788873100///10.*&#")068<AFMRX]`bdehlosstxxvxyyyxwy{{xuqopswyvv|‚‡‹Ž“”“’‘—†c*0:EQYVLB=<951.-.-)$=`s‡„†ƒ}wtqmh_UNHDA@FQ^houyy|‚ƒ†‰ŒŽŒž“*•¦•—˜™››œœœœžœž›š –|f;#'Qt¢¨£¡¡¢££¤¤¢¡¡¢¥¦©£œŸ ŸŸ¡£¤¥¥¤£££¤¦¨¨¦¥¥¥¤£¤¥¤£¤¥¦¦¨©¨§¦¦¦¥¥¥¤¢£¥¥£¤£ —”–†mY<&1;ADD@<744567547:<=;732/.//034666420,))**+*% !'-28<AHMQVZ_acegjmqtstwxwz{z{ywx|}{vsrqruvstz€„ˆ’””“‘˜ˆ`)/8AKOK@8579740/12/+% =^n€€‚|wtqojbYQJC?=BMWbkrx{~€‚‚‚„ˆŠ‹Ž‘‘‘‘— Š'—¨“•š›œœ›››œžžŸ¢¡•xY-4Zt£ª¤¡¡¢££££¡¡¢¢£¤¤¡ ŸŸŸ¡¢¡¢¥¦¤¢¢£¥¦§§¦¥¥¥¤¤¥¥£¢£¤¦§¨§¦¥¤¥¦¦¦¦¥¥¥¥¤£¤¤£ž˜—„kZ;'5=@A?;623458988<=>?>:640+)*+.0221../,'&&$!"+036;AGOTWZ]acehjkosuttuwz||{||wv{}{xwwvtrrqsx„ˆ’”””“•†]&*18<<6.((,010-.2441+%#<_ny}{vsqqog^VMD>;@IS]gow|€€€‚†‰ŠŽ’“’“™{7—ª—™žœœœ›˜••œŸš™¢¦—ˆpQ$#=h¦¬ ¡¢£¢ ŸžŸ¡££¡¢£¢¢¡ ¡££ Ÿ£¦¥¢¡£¦¦¥¦¦¥¤££¥¦¥£¢¢£¤¦§¦¤£¤¥§§§¦¥¥¥¦¥¥¥¤££˜s^8%4::976337:;<>>>@AAA?:51+'$#$&)+,*'')(#" (1689?FMSWZ]_cdfilosvwuttw{}|{|}yx{||{zywrnmmpv}…‹’””“’–ž€U"'-0/*#$'))+15760)$&D_q{|}}zvqpqpkaWNB;9=GQ[eny{~~}}‡Š’“’–—‰a;\˜¬˜› Ÿžœ›œ›™’‘›Ÿ™–Ÿ¥}kO(5s‰¦«Ÿ¡¢¢ ŸŸŸ ¢¤£¢¡¢¢£££¤¥¤ ž¡¤¤¢¢¤¦§¦¥¥£¡ ¢¤¥¤¤£¢¡¢¦§¦¤¤¤¥¦¥¥¦¥¤¤¥¥¥¥¤¢¡œŒ‚}b7!05545889?CDEFFEEEDB>82,%"!$%#!! '/7<>?ELPVZ]`bdfhlorvxxwvuvz|}||}{z{|~~}ytmgehmt|‰Ž‘””“’•ž}R'$)+' #&)-154/*&'D`s~}}|xqnoomdYNB:8;FPZcksz{|{yxz|€ƒ‹˜švG\r“ž”—ŸŸš›ž”˜š˜—œ„uaD!-A}§¯¥¤£¢ ¢¢¢¢££¢¡¢££¤¤¤¥¤ Ÿ ¢££££¤¥¦¥¤£¡¡£¤£¤¥¤¡ ¢¥¤¤¥¥¥¥¤¥¦¦¥¤£¤¤¤¢žž¡—ˆe9*467;ADEFGHHHHHGGC?:3-& $-4;?ADKPSW\_adffhloqtvyz{zyy|}}||}}||~~}xofa`dkry‡‘’’””“‘ ~P&"&&#!%(+--*(($'C`r~~}||xplmlje\RE;7:DOZckqxyxvttwzˆ Ÿa6j„™ž•˜›ž™™¡”—™˜–›”|oW8,Hx•±¶¥¦¤¢¡¡¢¢ ¡¢¢¡¢¤¤¤¥¦¥¤¢ ¡¤¤¤£¢¢¤¤¤¤¤¤¦¦¤¢¦¤£¢¢£¤£¢§¥¥¤¤¥¦§§¥¤££¤£¢›››‚f;(4:?DILMLLKIHFFFD@<60(!#-5;@CEIPTX\`acgiijlprtwz|}}|{|~~}~}|}~|uia[\bjs{‡‘““””’” {O% #"!"#$#!"&'$!! 'C`q{}|}yroolhe`YK?:;AJXbjqxyvusuy|„‹ŽŒ¦œ?{•§§™™šœ›š˜™œœ–”š›š—œ‘zoP,:Ktš¶®¥¥¥¤£¢¡ œŸŸ¢££¢££¢¥¦¦¦¥¢£¦¨§§¦£¢¢¢¢£¤¦§¨ ž£££¤¥¥£££¤¥¤¤§¤ž¢¦¦¥¤¤¤£¥¡Š‚gA!$3>FILNOOOLHDAAA@<83+##.5;?BEGLRW[_abehjlmosuvx|~~}}}~€€~}|zy{|zud\Y\dlt}ˆŽ’“”“‘’›wN& ##!#''%""##" (Cbq€}||yurqniec]PD=;<CS`iqxzxvuwz}ƒŽ˜’Œž”.Š © ›™™šš———››ššž›ž£’ujD("?Yœ¬¦£¤¤¤¤¤¤¢žž¡¢££¢¡¡¢¤¦¦§¦¥§¨¨§§¨¤£¢¡žž¢¥¦¥ž ¢£¤¥¦§¦¥¤£¤¤¤¦žš£¥¥£¤£ ¤¤‹‡‰„mH%/@JLNNOQQLEA>=><83.$#2<@CDEFHMTY]abeijloqruwx{~€€~~€€€}{yxxwug]XY_fmt~ˆ’““’”tO' #&))!!%&%"!"##!(@`s‚~||{ywuqnjf_TI@;9?N\fox{{{{||}‹–•“˜—…&5™«§œš˜˜™šš—’™› ŸŸœ ª™o[7!Ki‘Ÿ£¢¢£££¢¤¬«¬¬¦¤£££¢¡ Ÿ £¥¤¥§§¤£¤¤¥¤£¡ŸœŸ¥§¦¡¢£¤¥¥¥§¨¨§¥¥¥¤¢ ž§¥££¡›ž£¥ŽŒŠsO)$:GJKKLONHC@=;971*$,7?BEFEHKPVZ^abfkmnqrsvwz}€€}~€}}{zzvtqe\UV[ahls}‡Œ““’ŽrP')+-/.' !$$"'>`w€‚}{zyvrpnh^UKA<<?IWcmv{}€€~‰””™£Žs(H˜°žœš™˜˜™š—•˜¡¡¡ Ÿ›¡™kO+"Sq™¦¦ ¡¡¡ œ˜ª®¨¦¦¥¤¥¥¤¢ŸŸž¢££¤¦¦¥¤££¤£¢¡ž™™¤¦¦¥¥¤¤¥¤¤¦§§¥¤¥¥¤ ¢¥¤££¡ŸŸŸ¡£–••ŽwU#0@FHGIKJEA?=71-("(4<CEFEFLRV[^`bcgmpqqsvxy|€€~~€€~}{yxtnfYOMS\bgls}‡‹’’‘Ž‹lN&45653-$! ##!%=bs}ƒ‚|zywspmh_UJ>;>AHWcltz}‚‚„Š‘‘ ¡‚d7[‘§šœ›››™˜™˜—˜£¢¢¢£™–‘gI$%^|¡¦¡¡¡¢ž˜‘¢©§¤¤¤¥¦¥¥¤£¢¢¦¥££¤¥¦¥££££¢ ªª§§¨§¥¤¤¤¥¤££¥£££¤¤¤£¢£¤¤££££¢ Ÿ ™•”‹y[% /<BCDED@=:6-% %/8>EGGFIQX[]_`bcflqqqswyz}€€~€~~~}{xuof[NGJS]bflt}…ŠŽ’’ŽŽ‡iL%;;;:82'! !# $=^pwƒ‚}|zwsokf_VK>:>BIXdksx{~€‚ƒ…ˆ‹Ž¡šsUX|Ÿ¦š›››››™˜˜˜™œ§¢¡¢¨“ŠdE%/fƒ¢´©££¤¥¦ ž¤¥¤¤¤¤¤¤££¤¤££¨¨§¦¤¤¥¦¤¢£¤¤¥ª©©¨§¦¤£¤¦§¦£¢£¤£££¢££¢£¤£¢¡¢¢¢¡¡¡œ—•ˆx^#!2<?AA>:72* $07=AGIJKPX]^^_bdegmqstvyz|‚‚€~~}~}|{xtf[OGEJS\agmu|„‰Ž‹‹†hL$B@=:83*$ !!$>^nvƒ‚}zuqlgbZNA<?CJXdlsxz|~€ƒ…†Š¡˜Y8s”œ›ššš›œ›š˜˜£ ŸŸ¥Ž[? )4p†œ¤¦¥¥¦§¦¥¥¥£££¢¢¡¡¢¢¡¡ ¥¨©¨¥¤¤¥¥¤££¥¨¨§¦¦¥¤¤¢¤¦¦¥¤£¤¥¥¥¤££¤££¤£¡¡¡ ¡¢¢¡Ÿž›y_#$/6;:40*# ,7=?BFJMPTY^^_`cfgjnruwxz{}‚‚‚~}{zyeTKEDFMU]chnu}…‰‹ˆ‡„hH$B?942/)&# !&@^o}‚‚~{xuoje^SF??CKXdnuxyzz{}‚ƒ†‰§•@…ž©¥›š™›ž›—–˜šš›œ£ŸŽvP6+GyŸ©¤¦¤¤¥§¨§¦¥¤¢ ¢££¢¡¡¢£¦¨¨¥¢£¤¤¤¤¤£¤ªª¥£¥¤£¢¢¤¥¥£¤¥¥¤¥¦¦¥¥¥¤¤£££¢¡ ¡£¢ žœ”ƒe+ (.,% *6=@ACHNRUX[^`adgijlpruxzz{}‚ƒ„„ƒ€€}wtlUE<<AGOW_ejpw~„ŠŽ‹‰ˆ…„~jJ&850,*'%$"(A\qƒ‚€€‚€~|zytokbVI@=?HUcnuxxxxy|€ƒ„‡ˆ¢/’ Ÿžž™™šš——––“™™ ›x@,6Sƒ™¥¦£¤¢¢¤¥¦¦§§¥£ ¤¦¥£¡£¥¬«©¨£ž¡¢££¤¤£¢¥§£¡¢¢¡¢£¤¤¤¤¥¦¤£¢¤¦¥££££££¤££¢¡¡¡œš——”‡n7 %1;ACDDJPUZ\^`behjlmnqsvyyz{|~€€ƒ„…„‚‚€~tcSD748BJPX_ekqw|ƒˆŒ‹ˆ‡‡„€{mQ((&$!)AZo€~€€€|zzvqlaUIA<=GUbmswyyxy|…ˆ‰‰šŒ,E–¡˜žŸŸœ™˜š›™—••˜šš›•‡t0! ?`Ž § ¢£¢¢££¤¨©§¤£ £¤¤£¡¢£¥ª¨¨§¢ ¡¢¢£¤£¡¢¤£¢¡¡ ¡£¢¢£¤¥¥¥¤£¤¤¤£¢¢¢¡¡¢£££ ž›š˜–•”‰x?$0:AEHIIMRX^abdfikmnopruxyxz{{}€‚„„ƒ‚€lMA5/08CKRY_emruz‚†ˆˆ‡†…ƒyhS'(>Vl|}}€‚‚|zwqj`VLD@@GS_iqvyyy{}‚‡‹Œ•’w:d˜¡—££Ÿš™™›œœœœ››Ÿž¥˜~_G^‘¤¨Ÿ ¡¡¢¤£¢¤§¥£¢¡¢££¤¤¤£¡ž¢¦§¥£¢¡¡¢¤¦¤¢¤¤££££¢¡¡ ¢££¥¥¥¥¤¢¡¢¢¢¡¡ Ÿžžš•””Š}Z4*6?BEJMNPU[_bcegjmoppqsvwxyyz{}€‚„„ƒ€‚nZG;635;CJQX_enrtxƒ…‡†„„„{iP%!! '<Sjyz|}~€‚ƒ€|yuqi`YRKFCFO[emsvxz|~‡ŠŸ…^Vv–¢›› §›™˜šœžœœœŸ ¢˜tP"(Oc”¦§ Ÿ ¡¢¤£¡¢£¢¡¡£§¥¥¦¦¦¦¢¢¤¥¤£¤£¡ ¡¦©¦¦¦¥¤££¢¢ ŸŸŸ ¢£¤££££¡Ÿ¢¤£¢¡ ŸŸŸŸžœ—”””‰|eH%$0;BCEINPSY^adehiloqrrqtwyzzz{|}‚ƒƒ‚{jYJ<5348>ELRZahprrv}ƒ…ƒ‚„ƒiO& "#" ';Rfvy{|}€‚‚€|yvsmd\VPKFEKVahlpuz|}„†Š–tGl„“¡œš ˆŠ•›œœœœšž™Žb?6ezœ©§ žŸ¡¢££¢£¢¡ Ÿž¢¤¤¥¦¥¤££¥¥££¥¥¢¡¡£¥¦¨¥¥¥¤£¢ŸœŸ ¡ ¢¤¤¢ ¢¡ ¢¤¤¢¡¡ žœŸŸ›•”•‘‰lR%'3=CDEINRW[_bdfiklorsrquy{}}|}~~~€‚‚€€hVI;6436=DJPV^cjprps{€‚‚€„„gO) !!"#!&;Sesy{{~€€}zxuph^VPLHFIT_diouz}~€…‡‹—e3y’ŸŸ˜›Ÿž„…~‰“™››š—”–™“‰Z7&An†¡ª¤¢ ¡¢£¤¤¤£¢¡ Ÿ ¢¤¤¤¤¤££¤¥££¥¦£¢£¤¥¤¡¡£¤¤££¡š› ¢¢¡££¡Ÿ ¡¡¡¢¤¤¢ ŸŸžžžœ›˜—“wZ. *5>DEHKPV[^`cegjklnqsssux|~~}}€€€}znTF80245:CLQU[aekoqqsy~€€€‚„ƒ|fN) !! !$%# &=Xktz|}~€€€|ywskbWPNLIKU^djrvx{}„‡›R+‚™¤••™››‘“›ž›šš›š“”™¡„W((Oq…¥¥¢¢¡ ¡£¤¤¢¢¢¢¡¡££¡¢££¢¢£¥¤£¥¦¤£¦§¦¤¢¡¡£¥¦¦¦¤¢£¤£¡¡¢¢ ¢£¢¡¡¢¢¡ Ÿžžžžžœ›—“—y_6%%-6?DGJNSX\_bdfhkllmprsstvz}}||€ƒ„o\A:2/268=FOUZ^bfkoopsvz}€‚‚€{dJ& ""! "$$$!"$%#!(A`rz|~€~~€€}ywsog[QNNLNV`houxxy|€ƒ„”“..†š£•”–˜˜˜š›œ››ššœ –“œ ˆ}D1Yz…•›Ÿ£¢¢¡ Ÿ £¢¡¡£¤¤¤¤£¢¡¢¢¡¡¢¤¤£¤¥¤¤¦¦¦¥¤¢¢¢¥¦¥¤¥¥¤£¡ ¡¡ ¢£¡ŸŸ ¡¡ ž Ÿžœ›š•’‡yd?+&/5:AGJMQVZ^begikmnnoprsrsvy{|{|~‚~}yo[L;/+-269<GRY]bfimonoqtvx|‚}xcF$ !$&&%""!!#$%%%"!&*+)&#+D_t}}€€~}€|ywtqjaWRPNPV`jpuyxx{~‹D ¡–”•™™™™šš™™ššœ›˜“ž˜j3 9W‡”•—œ¡¡¢¢¡žžŸ¢¤¥¦¥¤££¢¡¢¢¡¢¤¥¤¤£¢¢£££¤¤£¤¥¥¤ž¡¥¥¢¡ ¡¢¢ ¡¡ œžžŸ ¡¢¢¢£¢¡žœœ›››•ˆyeJ0&/6<@FKMPTY\^bfhjlnnpqrrrqsvxy{{{|~€~~tdUMA80,.048>HT\`dhikmmkloqsx~‚{t_A" !$')**('%! %&'()(%!#(,.*'%"+D^rz|~€€€{yxvsmd[SNMPV^houyyy{}€‚?eš§—–˜›š˜—˜™˜™š›œ›œ›–‹kQ."I[“¤œšžŸŸ ¤§ŸžžœŸ£¥¦¦¦¥¤¤¤¤¤¤£¤¦¦¦¥£¢¢££¢¤¥¤¥¥¤¢¡¢¥¤¡ ¡£¤¤¥£¡ŸžŸŸŸ¡¡¡¢££¢¡žœœœ –Ž‹ƒxeM4#)28<AGLOQTX]`adgjklopqssrstuvxyz{|{{zxtiQH>73/-047;BLV^bfjijmnkilnot}€}zoS9" !!"$')*,-,*'#$)++--,'#"'*+(&$"'?[mvz|~~|{{zwrh]UOORTYbkrvwy{}€k\{ «œ™˜˜››˜—˜˜—˜šœœ ˜‹€R<!,Xjš¦–™žŸœ£¤¡¡¡ ¡£¥¦¥¥¤£¤¤¤¥¥¥¦§§§§¥£¢£¢¢£¥¥¤¦¢¡¢£¥¤¢£¤¦¦¥¤£¡ ¡¡¡¡¢¡ Ÿ žœŸ¢¦›’’‡yeM6$+29=AFJNQTX]abcfiklklorsssuxwvwxy{|{xupgWG<8643369<AIQX_cgjjklmljjllqz}{yxjE2" !#%()+,.-+(&#!&*++-0.)!&)%&'%! $'(&$$"%;Thty{|}€€~}|||yxh`XQQTSV`hnruy{|}ƒw^f…£¦˜š™˜šš˜˜™˜—–™œœ¢—Š~K4 7ct §˜›Ÿ Ÿ ¢££¤¤¤¤¥¥¦¥¥¤¤¥¥¥¥¤¥¥¥¥¦§§¤£¢£££¤¤££ ¡£¤£££¤¥¦¥¤££¢¢¢¢££¢ Ÿ ¡ŸŸŸžžŸŸª›““‡vdN7 (.4:AEGHLPSW]befgiklljknrtuuwyywvwxz|{voeQG913457:=@DJPUZafjlmmmnmkjjinuwvtqhG,"!#&)++,,*)(% $(,--/1/( $,5R9)))&!!$$##$$"$8Qbqtwxz|~~~}|zvjd[RQRQU_hlorwzyykHq‰¡œ”—•“™šš›Ÿœ˜—™›œœ¥™‹w>+&7u†¨³œŸ ¡¢¢£¤¥¦¤££££¤¤¥¤¤££¢££££¤¥¤£¡£¤¤£¢¢¢ ¡¤¤¢¢¢¤¥¥£¡ ¡¢¢¢¢££¡ž ¡ŸžŸ¡ •“†pbN6"'-3;CJKKKORTX^cefhijkljjlptvvwyywvwxyywuj[J<42579<?BFKNRV\bgjlmnnonlkhfioqppng4&# #&(*,,)))'! $).1110-("'-=rŠ)(&" !#$$##6O]flpty}{}~~~}}}zunh^TQPQT^hlmqwxww|T,$€Œ™•’’†˜š›œœ™˜™šššŸ—…j3",<t‰¨°¡¡¡ ¡ ¤¥¦¤£¢¢£¤¥¤£¤¤¢¢¢££¢¢¢££££¤£¡¡¢¢¡¡£¤£¢¢£¥¥£ ¡£££¢¢¢¡žŸŸœœ›œžŸ›”‹ˆ|g_N4$.15;AHMNNNPRTX\aehihiklllmorssuwxwvvwxwtiXL?5138:=@CGLQSTY`ehjlnooonlkifgjkklla !%'+..+**(" $(.221-($!)Rs†‘$## "### 0GUZ_chpssvz{|}~~|wph]SNMOR[ekmqvvuvu<5Š“–’“’‹˜œš››™—–——™œ˜‘{W0/Go„ £ ¢¡ ŸžŸ¡¤¥¤£¢¢£¤£¢¡££¡¡¢¤¢¡¡¢£¤£¢¢¡ ¢£¤¥¤¥¦¥£¢¢£¤£¢££££££¡Ÿ ŸŸŸžœžžš•’„xg[M4"-59=CGJLMNOPQTW[_ehfehjkkkklopqsvwwvuuvrhVH=7226:<?CGLPSVX]chjjkmooomljigfefimmb "%(*.0.+*'""%).0.+&"! ! !""""(;HORTX_fjovz{~€}xpg]RKIMRYbjosvvvwv.A‹˜–’“’“—™šš™—––—˜™”rL%:[“«ªžŸ¡¢¢¢¢¢¡ ¢¥¤££¤¤¥¤¡ ¡¡ ¢¥£¡¢¤¦¥¤£¢¢£¤¤¤¥¦§§¦¥£¢¢£££¥¦¥¤¥¥¡ž ¡ ¡¡¡ ŸŸžœ™•’‘‹~h[O7$1;@BFJMNOOPQSSUY\`efcdghghjiiloqsuuuutsriXI;4236;=>AGMRTVY]bgkmlmnoponlifeedfh|‰p !#%&&'**)'&#!#%(+-,*% !!"! !!!"""##$1<BFGJS_iov{}‚}woh^SJIMQW_hotvvww~ D‡’““’’’’“–™™šš™™™™œ¤“ˆlG7h’£·«¡¡¢¡¢£¤¥¤¡Ÿ¢¤¤£¥¦¦¦¤¡ŸŸ ¡££¢¢¤¦¥££££¥¥¥££¤¥¥¥¤£¢¢¡¡¡¢¤¤¤£¤¡Ÿ ¡ ¡¢¢¢¡ Ÿœ™™˜“Ž‹~k]R8$/:BEEILOQRSSSUVXZ]adeefgedehggjorrssttrni^M?9668;?ABEJRWYZ]aeiklmnnoponmidaceI‹ o!#%%#!!"! !!#$$$$$""&)()+*("!"## #%&%$#! %1=DHHJQ]hou{~€ƒƒ€wpj_UNMPQT[enuxxwzs<j—š’“’’“•–—™™™šš›š™Ÿ¨†{_=%Im•¥´£¢£¢ ¡¢£¤£¢ ¢¥¥¤¥¥¥¥¤¢ ¡¡ ¢££¢£¤£¢£¢¢¤¥¤¢¡¢¢¢¢¢¢¡ ŸŸŸ ¡£¤¢¡ ŸŸ¡¢¡ ž›™˜™’‰†zi^P6%1;CHIIKOQSUVUUVWY[^beggfgffhjhgkqttrqrrng]M?8789:<?CHLPW\^_afillkmppponnmidaeRi ª—i! !%',00/-(# #$&'#!(+)()(% !#$!&)*(%# !,=LUXXZ_ekqv{~ƒ‚€xrkaYRORPQXbmvzzvzd]†§§’’‘‘’–—˜˜˜˜™™šš™ª¢m]H//\yš§¤¡¡¡¡ ¡¡¡¡¡¡¢¥¤££££¢ ¡¡¡¢¢¡ ¤¤£Ÿ ¡¡¡¢£¢¡¡¡¡ ŸŸžŸ ¡¢¡ Ÿ ¡¡ žœŸ¡¡ žœš˜—“‹ˆ|fYL2$0:BIKKLNPSTVVUTTVY\_begfeggilligjpuutsroi_N>77:<=<<@FMQU\_`adhklklmprqommnjffFo‡¨¬Œ[!(./4:>?:0*!&! "%()%!!(*)()&# "#!"# %(('%"&4H`imlnnosux{‚ƒ‚~yrkb[SOPOOXcnw{zoRr•©¤”“’‘—™˜——˜™™˜™™«¢oS@(#7e}•©¦ ¡¡ ¡ ¢¡¡¢£¢¢£££¡¡¢¢££¢£¥¥¤¤£ ¡¡¡¡¡¡¡ ¡¡¡ ŸŸ £££¢¡ ¡¡¢¢¢¢Ÿœœ ¡¡ Ÿš–”’ŒŠcTF-!/:BIMMMNOQRSTUVUUW[_acffeehijlmkiimrttsqk`R@66:>ABBBEKRVY_ccdfilmmnoqsrpnnokbOs“œ¦‹Q$)09Hcx}uF !##"!!&))))&#!#%$! "$&%$!(7Og{ƒ|||zyy{~ƒ…„xsld\TPMJKUcnuwu{Y9Ÿ£š”•—•”˜š™—––˜˜———¦ tP4 +Ahv‰¤¤¢¢ Ÿ ¢¢¡ŸŸ¡¡¡¡¡ ¡£¥¤£¢¢££££¤¥¥¥¥¥¡ ¡¢¢¢¡¡¢¢¡¡¢¢££¢¡ ¡¢¤¥¥ŸŸ¢£££¢¢Ÿ›œŸ Ÿžœš–”‘‹‡y^P@')6?EJMMNOOPQRRTWWWY]_adfffgijjkmljhkorrrofWG:68=BDEEGJNTX]aefgilopppqrttronlhc¢Ÿ’–«ŒQ'2E~Ÿ¥¯ª˜z= $()('$""#$$" #$" &8So„€€~}}‚„††…€yvoe[UQKEHSbmsttuE%‰§£˜—™™™œ™š™—•••••—™£šlE).Gs€•¤¢¢¡ Ÿ ¢£¡ŸŸ¡¢¢ ¡¡£¥¥£¢£¤££¤¥¥¤¤¥¦¢¡¢£¤¥£¢¢¢¡¡¢££££¢¢¡¢££¤¢££¡ Ÿ›šœ››š™—ˆ‚n[K;"",3<CGKMOQQPQSUTVYYY\_abegffgiijlmmkijmoomg\J<668=ADEFHNRW\`ehijmoppoprsutrnjecX °Ÿ“™«…L$1E}‘™ª¸À»ªƒL"'(&&# !%$#" " $6So|~€~}}ƒ………„~zqg\SNJFGQ`lssqg=#‘« —˜™˜š™˜——–•““’“– Ÿ]: :Y…˜¥§ž Ÿ ¡¡¡ žŸ¡£¡Ÿ ¢¡¡¢£¢¡£¤¢¡£¥¦¤£¡¢¢£¤¤¥¥¤££¢¡¢£££¢¡¡¢¢¡ žœœžžžžžžœ››š™™š›Œ‚{jXI:"!,48?FJKMPQQPQSVWXYXZ\_aceedfggfilmmkihjklg\M>779;=ACEGKRW[]afhiknpqpooqrsrojd]Y6š¨”“šª{F$0@{Œ—¨²¸¸®žr $'&%""$$# %7Umy€€~}~‚ƒƒ„„ƒyqf\RLKIGM\irskb2™®•–˜˜—’Ž‘•—–”“’’•¨›†R7&BUŒ ¢¡Ÿ ¢¢¡ Ÿ ¡¤¥¢Ÿ£¢¡ ¡ Ÿ¢¢¢¡£¦§¥¢¡ ¢¤¥¥¥¦¥¤££¢£¤£¢¡¡¡¢£¢¡ žœššœž›œžžžœš›šš™œž’„}jWH;##07<?DKNNNORRRQSVXXXXZ\^adeddfffehlmmkihijjbO<55:=>AEHLORX]`aehjkmpqppppqppnkg`XS,‡ž•“š©{G&0>ƒš‘›žœœ¢°»¿º¨‡4 !%&%!"##%$! '8Rgs|€z{|}‚ƒ„€{rg]SMLIFKXhrska* =¡±”’”–•’•™™–““””Ÿ‘xH-%I]š–Ÿ¡¡¡¢¢¡ŸŸ¡¤¦¦¤¡£¤£¢¡ žœ ¢¢£§¨§¢¡¡¢£¤££¥¦¥¤¤¤¥¥£¡ ¡¢£¤£¢¢ œš™œŸ œœžŸ›œœœš’–•ˆ€nXI;##-6;?CHMOOOPSSSRSTVWWWYZ[^ceeeeddegklkjiijjg\D437<>AEKORUX]`cehklmopppprrrqokfb]VPMr‘œ“™¦zF!+1&ˆ‘›¢žž±¾Ä¼¦z:"## #$'('$" "#"&7O_lttsstvx}ƒƒ€|vi_VOJHFKXgrrp_#9\£·–““––Ž‘’žž—”•–˜—ƒi;#!4`q”ž™›ž¡£¡¡¡¡ Ÿ £¥¦¤¤¥¥£¡¡ ž™Ÿ£££¥§¥¢¡¡££¢¢¢¤¥¥¥¥¥¦¦¢ Ÿ ¡¢££¢£¢œššœŸ žŸŸŸžššœœš’‘ŒxnZH9"%1;ABEHKNPQQRSSRSSSTUTTWYY\aeffecbdgjkjijjkg]M8/37:<AIPTWY^adgikmoqqqqqqqrqolf_[XSOWcƒŸ˜š¤tB -4)‰ž–Ÿ¤ ŸŸŸ ¨°·¹²¥w !!!#$&*+)'&##$#!#0EU_eggiklu{€€‚ƒƒ€}zk^WQJGELYfqqlWSy¡¬˜•”—™–“•ž¢›•––¦œ~a: (Et}œ¥œœœŸ ¡¡ Ÿ ¡¢£££¥¥¢ž ŸŸ¤¤¤£¤¥¥££¨¬©£¡ ¡¢¤¥¦¥¤£¢¡¡ ŸŸ¡¢¡¡¢¡žœžŸ¡ Ÿž›™—–˜›š˜–Œ|oiZH6#.8AFFHKNOPQQRTSRRQPPRQRUWWX\acdcbbcehiijlljbN9//59<?DMTWZ^beghjlmpsrqrsrrrpmg`YTRPOWYrœ™¤¤n:+9AŠšœ¢¡¡ ŸŸŸŸŸŸ «¹ÂÀµª¥£§«¦V!#$&)*)('$!""! +>JRXZ[_emtz~‚‚‚‚}{k]VQKFBIXfrqgK_‰žŸ˜–”—š˜‹“š™›žš—™Ÿ—rT+/Fz~§œœœž ¢¤£ ¡¢¢¢£¥¥¥£ ¢££££¤¥¥¤££¤¤¥©©¨¥¢ ¡¢£¤¥¢¡¢££¢ Ÿ ¡¡¡ ¡¡ ŸŸŸ¤¢Ÿ™•˜—•—š™——ŽmgYF0"0:AGKKKNPOOOPRUSQPPOOPQSVXWUX]`bcccddgijlnmg\A,*/6<AFKRX\_cfijknoorsssttssqlf^UMJMNQX]aš£¦¦g0,?X„ž£Ÿ¡¡ŸŸŸŸŸ¡¡ žž«¹ÀÁÁ¿ÀÅÇ´—t9!! "%'()((('% +;EKNORXcnu{…†„~}}{j]VRJ@<DVerqc@a†˜ž—”“—š˜•—œ›œœœš™œ“ˆY;.R„¢§žžž¡¦ª¨¥¥¢ ¡¤§§¥¤£¤¤£¢££¤¦¤££¤¥¥£¦§¥¢¡£¤¤£¢ £¤¤£¡¡ ¡¢¢¡ ¡ žž žœ–˜š˜–—‚pdWE-+8@DJNNOPQPOOORSQNNOPQRTVXZYVVY]`aabccehkmnkaQ:+,28>DJOTZ_aegjlnpqprstsrqqqmd\SKFGMQT[bW—¥ªd+2DI}¡Ÿž Ÿžžžž ¡ žžžŸ ŸŸž¡«²º¾Àº”S "&**(&((&$"-@LOQQSYcnu|ƒ‡‰ˆ{€}i]TPG<6?SbpnZ5sŽœ—•”—š™—™ž›š™žœŒ‚Y6<]‡—§§ ŸŸ ¡¥§¨§§¥¡ ¢¤¦¦¥¥£££¢¢¤£¤¦¥¤¤¤¥¤¡¤¦¥££¤¥¤£¢ ¢¤¤¤¤£¢Ÿž ¢¡ ŸŸ¡ ŸŸŸŸžŸŸž›™š—•™‚rcVF.+7CILQUUTSRPONOPPNMMORSUWXY[ZWVX[]^`bcdfilmmfVB4./4;CJMPV]behjlnprssstutqonkcXOHA?FMSY_cT‡œ´®i+#9A?}‘ žžžžŸ Ÿžžž ŸŸ ¢©±¸Á¿² l !%)++))*)%!&3L]`cbbbhow~ƒ‡‹ŒŒ†„Š„e]SOH=5<N^lhL)¦¢––••—™˜—šœœ›™˜™£›‚yU/Cd„—§£ ¡¡£¦§¥£ Ÿ ¢£££¢¡Ÿ ¡¡¢£££¤¥¤¥¤¤¢¡£¥£££¤¤¤¤£¢£¤£¢¤¥£ Ÿ¡¡ žŸ¡¡ ŸŸžžŸŸœœœŸ™––zoaTD-*5=EJMRWXWUROLKLMLKKMNPRTVWYZXVVXYY[^`acfjlkg_M;4349@INOQW_dhkmmmprsttssromibWG><<=DKRZ`d`{–³¬h)%?Q^‡“ ¡žŸŸŸŸŸŸžŸ Ÿ ŸŸŸ ®·½¸£ˆY ! #&),,++*(%("+;Ymxwwvuuw{~‚…‰ŽŒ‹”Žq_RNI?58GXgc@%š°’’“””•—˜™ššš™˜˜˜¥•ufH$$Pp‹˜£¢¡¡ ŸŸ¡£¢¢ ¡¡¢¢¢£¢¡ŸžŸ¢¢£¢¢££££££¡¡£¤¤¤¤¤¤¤¥¥¤££¢¢££¡ ¡¢¡ŸœŸ ŸžžžžŸ››ž ¡š•~tk^QB+,6>DGIMRVXWTRNJJKJHGIJMOQRSUXXVTVXYYZ\_acgjjf^RB658;@FMQSV]dhlopooqrtssrqpoldU@0.38>CHPX`efm‹±©c%$B_z—¤¡žžžžŸ ŸŸŸžž ¡¡¡¡ ŸŸŸ ¡«³»ÀÁ»¦Z !$(),-,+)'#p$/Bas|€|~~}~€ƒ‡ŠŒŒ‹’Žw`QKIA56BSc^5, ªŽ’““”••˜™™™™šš›¥”nY9!!0^v“žŸ ¡¡›ž ŸŸ¡££¢¢££¢¡ ŸŸ¢££££¤¤£££¤¤£¤¤££¤¢¡¢¢¥¦£¡¡£¤£¢¡¢¤¢ žžžœ›œžžžŸššž¡¢™›˜…{i[P@'(7>FJKLPUXXURQNJJIGEDEFHLNPQSVWVUVXYYZ\_aadge^QE;68<AEKPTV[afilnoopqrrrrqonnmbK4+,29>BFMU^eje}©®c-"DdyŽ™¤œžœžŸŸžŸŸ ŸŸŸ ¡¢¢¡ŸŸ ¡¡¡¢¡ ¢´º¾ÃÁ«Œ[ ! $&'+.-*'%!-s’¦®ÄÈ–0 $2Ibv‚‚‚€„‡ˆ‰ŠŠŠ†n]OIHB57DQ^X+%@¡••””””““”—™ššœœœž‘hM*'3lz•¦£ ¡¢ ›œžŸŸŸŸ ¢£¢¢£¤£¢¤££¤¥¥§¤¦¤¥¥¦¦¦¦¥¥¤¢¡ Ÿ£¥£¡ £¤££££££¡ŸžžœœžŸžžžž›šœ ‘•„~eWO?"!5BHNRSSVZ[XTQPMIFEDA@A@AEILOQTVXWWWWXZ]```bb[QE;77:@EIOTWZ`fhjmooopqppppnljklaC,)-39>BHOV^djns¯³`$"?gv‹£œœžžœœžŸ¡¡¡ ŸžŸ ¡¢¡ ¡ ¡¡¡¡ ¡¨¶Áû¥„B!"#" !#%&*,*'%% 7wž¯¸¾¾Á¬d+!$"!"" #0G`s~€‚‚ƒ…†‡ˆŠŠˆ†‚e[OHHB56EPZR#(Z¡®›˜——–•““•—š››œ ¡—‹Y3'H|…•£¤¢¢¢ œ›žŸ ¡¢¢££¢£¦¥¤¥¤¥¤Ÿ¢¤¦¦¥¢¢££¢¡ŸŸ ž¡¤¤¢¡¢££¤¤¢¢¢¢ŸžžžŸŸ œ›šœ“‘}wdWO> /@KOSXYZ[[ZWSPOLHFEC@??==?ADJNQSVUTTUWY\^_^^XND;789=BGKPVX\afhjmnnnonllmmkifhlcA)(.37;@FLT]cjni©²W#=o…£œœžœœŸŸžŸ¡¢¢¡Ÿž ¡¡ ¡¡ ¡¡¡¡¡ ¡¡¡¡£²¾À¼°ˆH "#$%"!#%'()'$$% 9v”§®®¯±³±²¬µ·©v"%$"#$$"!!$$!!.C[r…‚‚‚‚€€…ˆˆ‰Š‹Š‡„ƒk[OHH@22BMUI;m¥ª˜™˜—–•”•—˜šœœ›¦ ”‰V.3Z…‘œ¤¡¢¢£ œ››Ÿ ŸŸ ¢¢¢¢¢¤§¦¥¦£¢¡ ¢£¥¤ œ ¡¢£¡ŸŸ ŸŸ¡¤¥£¡¢¢££¢ ¡ŸœœžŸ Ÿœ™››šš››™•™‘wvgYP=+>IPRUYZZ[ZWTQPOMKIHFCCC@=<<>DJMNPQQRTWXXYZXSK@977:<@EIMRWY^dhjjlmlmmljijhec`bibC*)/59;>DLV^cjma›²\#<u“–š¡œžžžž žžŸ¡¡ ŸŸŸ ¡¡ ¡¢¡¢¢¢¢¢ ¡¡¡¡¡¡¡¤²¿¾´§{ !##$%"!%(())&$$#%x£³¹½¾»·¶¶«œ¢³¶£g #$#"#%&&''%&(-/.'*=Sn†„€‚‚„‡ˆŠŒŒŠ…‚~o_QJG>/->KPARª –——•“””–˜™š›œœ›“Š~N'$Bb‡“¢¥ ¡¡¡ žœš›ž ŸžžŸ¡¡¡ ¢¦¥¢¢ ¡£££¢ŸžŸ ¡¢¡žŸ¡¡¢£££¢¢¢¢£¢¡¡¢¢¢¡Ÿž Ÿžš˜˜˜˜™š›˜–¤nsgYP=4FNQTWYZYXVTSSRQQRQPNNOMIC@<;@EFGIJMPSUTRQPJB<77:=>@CHLOSWZ`ehiijkjklkjigc`]Z\e`C-,17;<=@IU_chklX$>t’™› žœžŸŸ ŸžŸŸŸŸ Ÿ ¡¢¡ ¡£££££¢¡ ¡¡ Ÿ ¢¯µ·½¸—a !!"!!! !&)*++(&$"&No¦«®°³²¬¡˜–––—™ª³³‰#%%#! !$%(**()-3773('9Rf{}€€{|ƒˆŠ‹Šˆ„~qaSKE;+*;IM;fˆ¥––”’‘‘’“™››œœ¢˜‰€n8$Qg…’£¤Ÿ ŸŸžœšœŸ žŸ ¡¡¡¢¢¡ Ÿžž ££¢ Ÿž ¡ ¡¡Ÿ ¡¢¢¢¡ ¡¢¡ ¡¡¢¢¢¡¡¡¡ ŸŸ Ÿž›™˜˜˜˜š›™– †nqeXP<+ANRUXZ\]\XSRTWVUWZXVWWUTPJFCA@?>@DFINOPOLID<5027?DEFINRTVY^cfiighiiikkif`[WTRV`[@-,17<==>HU_bgkp{‡P$>nŠ™ŸŸŸžžŸ ŸžœžŸŸžŸŸŸŸ¡¢¢ ¡¤¤¤£¢¢ Ÿ ¢¢¡¡ Ÿž ££¡£½¿·£\ !$&(**(&%!Xem}£´»ÀÁ¾º´§žšš˜—••—™¦©«™>-,'# "&+**,16:;9/ #-BT_ilmprps{€†ˆˆ‡†ƒ€scVME9)(:HK7w‘¢ —–”’“—šœœ¢–Š{]/2`p‰–¥¡žžŸ ¡ ŸšŸ¢¡ žš ¢¢ ŸŸžžŸ¡¢¡¡ ¡¥£¢¢¢¡ ¡¡¢¡ ¡¢¡ Ÿ¡£¢¡ ¡¡¡ Ÿžžœš™™™™™šš˜šŠwoaUO;#8GPTX[]]__ZSRUYZY[][[\ZXWVRMJHC<:;?CFIIHGC=84104;DIIIKPUWY]adeffeefhjlkhaWLHHLT^W<++06:=<=FT]bfmprsJ%@k…™¢ŸŸžŸ Ÿžœœž žžŸŸŸ¡¢¢ ¡¤¤¢¢¡¡¡ ¡¢¡ žž £¢ ¡¥¶¸º©l!#%((&$'' pš¥¨³·¶¶¸³¯¬¢™˜™šššš™™—––›²¸žU64/'"#')*.28;;91#!&5FTZ\^eknry€„…„ƒƒ‚‚€qcVOF9('8DE3€™£œ˜—–”’‘’”—š›–‹nH!#>ev’›£¢¡ ¢£¢¡¡¡¢£Ÿ¡¥£¡ Ÿ¤§¢ Ÿ ¡¡¢¢¡ ¡¢¢¢¡¢£¥¤££¤£¢¢££¢¢£¤¤£¡ ¡¢¢¡ Ÿœœ›œœ›™™™››xk[QM:,BLSW[^^\[[YUUWZ^___]]]\ZZZVPJE?979=@BCA>;62//026>GLMOPQVZ]`bdeeeeeegiigbYJ=7=IT]S6(*05:??@HS]ciprpn1&El–¥ žœž ŸžžžžŸ¡ ŸŸ ¡¡ ¢¢¡¡¡¢¢¢¢¢¢¡¡¡ ¡£¢ ¡¡¡ ¢®¹¶¥ŠE !!"%')&#!'0$O…°Á½¹¶¬¤¡›œš˜™š™˜™šš™˜˜™™™Ÿ¦¦˜jC@:0'"%)+.16:85. #(3CPUSU\djp{‚…„ƒƒƒ‚‚€ocUNG9(&7@=-ƒ™ž—––—˜˜—•““–™šœš“ƒV.+Ii|™žš¡¥§¤¤£¢¢¡¢£¤©§¥¢ ¡¡¦¤¡Ÿž ¡¢££¡¡¢£¢¡ ¡£¢¢¢¡¢£¤¦¨¦¤¥¥¥¥¥¤£¢¢¡ žžŸžœ›šœžšš™œ™ˆugWNL91GPU[^_^[XVXYZ[^abba^^]\ZZXSKD=8447;>?;6/,*++.26:AJOPRTUX\_`bdedefgggfeaZNA508HU\P1&+05:@ACIQ[elqqmh%Adx’¥žœ›œžŸŸŸžŸ ŸŸŸ Ÿ Ÿ ¢¢¡¡¢¢£££££¢¢¢¢¡¡¢¢¢¢¢¢¡ ¡°»¿»°Ÿd !!##""!$')(&$$'\…¯º±°¯œ———–—––˜šš™™™™™™™˜——™¯¸¯{RME:1($*,-.363/)$-;JUZZZ\U\\y‚ƒ„„……„ƒ€seVLC7''6<6&”ž–—–•–———–••—™™žš‹yF(.Sr„Ÿ —œ¢¦¤£££¢¡¡¢¥¦§§¥££¤¤¤¡ŸŸŸŸ ¡¢ Ÿ¡¢¡ ¡¢¢¡¡¢¡ ¢¤§¨§¥¦¦¥¤¥¥¤¢¡ žžŸŸŸž›ššŸžœ›šžugVMJ75JRX^a`_]ZZ[]aaacdba_^]\[YTMF@953358:94/)$%(+.4;AGMQPRUWZ^`abdeeghjhea]UK?71-5ERYN/%,26;??@FLWckpoja#7Ot ¬œ››œœŸ ŸŸŸ ŸžžŸŸ ŸŸžŸ ¡¡¡¡¢££¤¥¤££££¢¡¡¢¢£££¢¡¡¢¦±³¼¿º²§š€Z !"###%$# #&'()))1kŽ¹¿ž™š˜——–•–—˜˜š™˜—™™˜—˜˜—•˜©³ˆSME=6,!%+-..02.(!&5K]komjlpu~‡†……‡ˆˆ†ƒ€viXI>1%%25.:¥¨“—˜–”‘’“”•–——§ ƒn7&;V~‘¤¢˜šžŸ¡ ¡ ŸŸŸ£¤¢¤¤£¤£¢¢¡¡¡ ŸžŸ¡ ¢£¡¡¡¡ ¢£¢¢£¦¨¤££¢¢£¤¤££¢ ¡ žŸ Ÿœ›š™˜šœœœ›š›‰‚whYOJ65JSY_ba`__``accbcca`_`_^^ZSKC<8542100.***().027?EKPQOQUX\_`acedegijf`YRI?72/,3AMUK-"*16:<;;@EQ_innh["9Ii¨œœ›š›œŸ žžœžŸŸŸŸžžŸ ¡¡¡¡¡¡¢¤¤££¢£££¡¡¢¢¢¢£¢¡ ¡££¢¤©©µ¼½¾½·ª’V !$%$$# !#$%()'v—¦¦›š™˜——–•–˜™™™™——˜˜——˜™›©³´¢aOJC<6.$&+.//0/*!(=\s…Š…€ƒ„‡ˆ‡††ˆŠŠ‡„~ujXG;-"",,$$1£©“•–•“‘‘“•––“z_6,FU‚™¨« ¡žžŸŸžŸ £¤œœ¢££££££¤£¢ Ÿ ¢¢¢¤¥¤£¡ ŸŸ ¡£¤£¢££ ¡£££££ ¡ žŸ¡¡ž›š›™™›œ›™™™›œ†sf[RL65KT[`ca`__`bcdcbba``bba_]YPID>:50-)((&%(+-0458<BIPTSQRVY]_`acddcfhhc]TH<62/,*1?JSK,(27:<=<?CN`jnni[!7K_£¨›œœ›››››œž›œœœžžžžŸ¡¢¢¡¡¢¢¢¢¢£££¢£¤¤££££¢¢¢¢¡ ¡¢¢¢¢¢¢¢¥£±¼Â¼”q!$"""!!"! !#"#%%–°¤œššš˜˜˜–•–™™˜˜™˜˜—––—™©°¯°¨€GD=84/&$)-121.( *@azŠˆ†‡‡…„†‰Š‰‰‹ŒŒ‰†‚vjZJ<-" '&$O¢““”•”“’‘‘“–˜–“ŒaJ)*Xfˆ™¦§ Ÿ›››žžŸ ¢¤¢žž¡¡¡£¥¦¦¤¢¢¢¢¢¢¢¢£¥¦¥¢Ÿ¡¡¢¡¡¢¢¡ žžŸ ¢¡žŸ¡¡ žœœžœ›››™™›œš™˜˜šš‘€gd\SL54LUZ`c`___`bccbba``addb_ZUOLKF@7+" $'*/25777:@DKRVVSSUW[^__`bccdeb]SH>8520,)/;FPH)+578;>?@BOajmmi\!;Uk¤¬œœ›››››ššœœœ››œœžžžžŸ¡¢¢¡¡¢¢¢¢¢¢¢¢¡¡£££¤¤¤¤£¢¢¡ ¡¢¢¢¡ ¡¡ Ÿ¡¡¢©§³¼·£h ! !!#$#!#"!%«·š™š››™™™˜˜˜™™˜˜™š™˜—–¥»À±y!"@=720-& "'+/00.'"-B`z‰…‡ˆ‡‡‰ŠŠ‹ŠŠ‹‹Š‡wk]O@0##!9l§®”’’•—––•””–™˜•ˆW=/ax˜ Ÿ ŸœœœŸ ¡£¤¢ŸŸ ¡¢£¦§¦£¡¡¢££¢¡ ¡¥¦¦¥£¤££¢¡¡ ¢¢¢¡žž ¡ žž¡¢¡Ÿœœœ››››š˜šœœ›™™š•‹}ig\TM43KUZ`ca`aaabbaacbabceed`XSPPQLB7(&,17:=>=;>BGMRVWUUUTW[_``abba_[SG;5431/+(-6@JB%-778;???@N`jmmhZ$>Wq¨°œœœœ›š™›žœ›œžŸ ŸŸžŸ¡££¢¢££££¢¢¢¢¡¡¢£¤¤¥¤¤£¢¡¡¢££¢¡ ¡¢¡¡¢¢¢¡¢¨¯µº³™w:!! "####" "#"-¬¶ššš›š——˜™™™˜˜˜™™˜˜™ž¢¤®} yŸˆ;74/,+*&"#&)+,.,'"/C]wƒ†ˆ‡†ˆ‹Œ‹‰ˆˆ‰‹Œ‰zm^PB2#En£¦•’‘•—————–—ššŠ‚Y2#6i…™œŸŸ ¡¡ žžŸ¡ ¡¢¢¢ Ÿ ¡¢¢¤¥¤¡ ¡¢¢¢¢ ¤¥¤££¢¢£¤£¡ ¡¡¢¢¢¢œž ¢¡žž¡¢¡ ŸŸ¡œœ›™˜šœž›š›žš†ke[TN5,DQX`bccecbbbabcdcdfgge`YUTSQG7+# &.38=?ACB@@BGMPTVVVURRW\^____]WND<5232/,)(+2;E?".889>A?<=M`jnmeR)ATi§°›œœ›™›žžœœžžŸŸ ŸŸ ¢£¢¢¢¢¢££¢£¢¡ ¡££££££¢¡¡¢££¢¡¡¢¢¢¢£¢¢¢£¢¡¡¨´¸²¥k !! "$$#! #&/Jd®›šš›™–•—™š™˜˜˜š™˜§·½± M©¼°„2-)'&&%#"#&''''('$#0Ed{†‡‡…„…‰Š‰ˆ‡‡ˆŠ‹ŒŠˆo]OB1"RrŸ¥•’’”••—˜˜˜˜š¤ž‡~P(8y›£¥¢ ¢¢¡ ŸŸ ¡¡¡ ŸŸ¡£££££¢ ¢£¤¤£¡ ¡£¤¤£¢¡¡£¤¤¢¢££££¤¦ ŸŸ¡¤£ ¡£¢ ¡¡Ÿžžœžžœššš›‹kcZUO7$;MY`cdedddeedefgffghhea]XVQG8( $-49=ABCEDA@BFJMORTSRPOPTXZZZWSK=100/01.*(').6B=#/:99=A?=?NbknkgH-CP]¥±œœœ››ššœžžœžžŸ ŸžŸ ¡ Ÿ ¡¡¡¢¡¢¤¤££¢ ¡¢¢¡¡¢£¢¡¡¢£¢¢¢££¢££¢¡¡¢¤£¡Ÿ¡©¯³º²S !""" &t…¡¢š™šš˜–•–˜˜˜˜˜˜š¡£¤©©Ž? S€¨¯ž*.*'$$$$$%'))&#"! $/Glƒˆ‡†„‚ƒ†ˆˆˆˆˆ‰ŠŠ‰‰ˆn[LA0 e~˜””“”••–—˜™™š©žn=*M…’• §¤¡¡¡¡ ŸžŸ ¡¡¡ ŸŸ¡¢¤¤¤£¡ ¢¤¤£¢¡¡£¤¥¥¤£¡ Ÿ¡¢¢£¤£¢¢¥¦¢ Ÿ¢¤£¡ ¡££¢ ŸŸžŸŸŸŸŸžœššš›•ˆŠld\VO9-FWadddbcdfhihhiigfffdb`\TG4$$*17<?DFFFDA@ABEFGILMMLKIKNPPNJF=0++++02.)((*,1><%.7669=>?ANcklh`=.ERa¡ªœœœš™™šœœžžŸ Ÿžž ŸŸžžŸŸ ¡ ¡£¤£¡ Ÿ ¡¡¡ ¡¢¡¢¢¢¡¡¢££¢¡££¢¢¡¢££¡ Ÿ ¯º¶¤„@ %›¤™˜˜™š™˜–—˜™˜—˜˜¥µ½¶Š0$‹¡¤”¡§ƒ)'%$$%&%&),*% ! %/Fl…†…„ƒ„†‡‡‡ˆ‰‹‹‰…††‚nZLB0 rˆ•””““”•–—˜ššž¦œ|b-&>Xƒ”š¤¦£¢¢£¡ŸŸ ¡¢¢¢¢¡¡¡¢¢£¤¥¥¤¡ ¡£¤£¢¢¢£¥¥¥¥¤¢ žŸ ¡£¤¤£¢¢¤¦¤¡¡££¢¡¡¡¢£¢¡¡ ŸŸŸžžž›š–Œfe_XO9">Uadddbbcfillkkkhfdddc`VI5#$-38=@CFIIHFEDCAAB@?BDEEDCCDD@<:71*'((*02-(&(++.98$ -468:<>ABMblkeU3,DTgœ¢›œ››šš›œœœžŸŸŸŸŸŸ¡¡ žŸ Ÿžžžž ¡ ¡¢¤¤¢ ¡¡¡ ¡ ¡¡¡¢¢¢¡¢££¢¡¡££££¢¢¢¡ ¡¡Ÿžž ¨²´°¡^ #Ÿ¯œ—–˜˜˜˜—˜™˜–—˜¥®°¬™c&o¢ª’’“£¨›`###$%'('(*-+%"!!&/Ei|„‡†…††‡‡‡‡‡ˆ‹Šƒƒ‚oZLB0!{‘Œ–––”““”••—› ¡—v[+/Rh‚–˜ž¤¢¢££¢¡¡¡¡¢¢££¡¡¢£¢¢¢¢££¢¢¢¢¢ ¡£¥¥¤¥¥¤¡Ÿ ¢££¢¡¡¡¢£¤¤¤£¢ ¡¡¡¡¡¡¡¡ ŸŸžžžŸžœŸŸ••`d`YO70L]cccaabeimnmlkhdcccaU? !*39>CGHHIKHFFEB=;<;:=>>>==>>7.').,'&&').0,'%(+,-30" -47;<=?AAK`jlfS/*ATgœ¥žœ™š›œœœ››Ÿ¡ Ÿžž ŸžžžžžŸ ¡¡¢£¢ ¡¡¡¡¡ ¡¡¡¡£¢¡¢¢£¢¢¡¡¢£££££¢ ¢¢ žžŸ ¢±¹®iO¦¥˜˜–––—˜˜™™˜˜¦µ¹ j‹¡ š’•¥¯©|1 "%'())),-+% &1Gf{Š‡†„…†‡†‡ˆ‡ˆŠ‰€‚‚€q[J>. ƒ™–š—–”””””™œ¢—„rS-[x‹”™˜š ¢£¢¢¢¢¡¢¢£¢¡¢¢¢¢¢¢¡¢£¤¤¤¢¡ Ÿ¡£¥¥¢¤¦£¡ž¡£¤¤¢¡¡¡¢¥¥¥¦¦¥¢¡ ¡¡¡ŸŸ ¡¡Ÿœ›› žŸŸ¡£Ÿ’’Žcc^XN6 <W`acbabeilnmllidb`\]?"&09@EJMLJIJD>==:41234688788:7+#&%$%'*,-+&$*///0* !-49<<<=>@J_imhT+)>Rj©ž›™™›œœ››ž Ÿ ¡¢¡ŸŸ ¡¢£¢¡¢¡ ¢££¡ ¡¢¢¢£¢¡¢££¢¢¡¡¢£¢¢£¤£¡¡¢¢¡ Ÿ ¡¢¬¹ÀµŒAB•°ªšš™—––—˜™ššž§°°¢…(Iœ«˜‘‘‘’““š¨«¡["&)+,,*+-*%! !&3Kfx‡…„„†‡ˆˆ‰‰ŠŒ‹†ƒ„ƒ€sZD6)ƒ™˜œ˜™—•““’’œ£”wwkH$6a˜ ¢¡ŸŸŸ ¡¢£¤¡Ÿ¡¢¢ ¡¢¢¡¡¡¡ ¡¢¢¢¡¡¡Ÿ¡¢£¥¤ ¡ ¢§¥¤£¢¢¢¢¡¥§§¦¦¥¤£¡ ¡ žž ¡Ÿœ›šœ žœ•–•daZTL4$FY_ccbbfiklkiihd]Q>()6?GKMNMIFD>7541,''*,/3226;?<. #%$%&(+,+(%'06520+ "08:<<;;<?K_jmgN&)>Tn£œ›šš›œœœŸŸŸžŸ ¢£¢¡¡¡¢££¡¡¢¡ Ÿ ¢¤£¡ ¡¡¢£¢¡¡£¤£¢¡¡¢£¡ ¢¥¤¢¢¢¡¡ ¡¡¡¡®®«QM•³·»µ¦›–—™˜————˜˜¤¸¼±”G ! p‹ ¥‘‘’’“””“—£²©’xD %(+,,+**($! !!$3MerŠ†„ƒ„†ˆ‰‹‹ŠŠ‹‹‰‡‡„€sY@1%†™™š™›™–“’“–š «švp]7-Eo†Ÿ©¢£¡¡ ¡¢£¤¤¢ ¡¢¢ ¡¡¡¡¢¢¡ ¡¢¡ ¡£££££¤¤ ŸŸ¡¤¦§¥£¢£¤¤¤¦§§¥¤¤¤£¢ ¡¡žžž ¡ ŸžžŸœœ™“••’’“maWQK50O^ddddfiiihd_c`O2!!'1>IOOLJIHE@;7785,$"%(,-)+4?IG:,# #%&(+,+'%*6=:51-"#2;<<<<;;>LakmeD"(?Uv¤œ›š›œžŸŸžžžžžžŸŸ ŸŸ ¡¢¢¡¡¡¢¢¢¡¡¢¡ ¡£¤¢¡ ¡¡¢¤£ £¤¤£¢¡¢£¡Ÿ¡¤£¢¡¢¢¡ ¡ ¡£¥®³¨A "'I¨ÀÍÏÊÆ»¸·´©Ÿ˜•–—™¢±©—V !""! “£ ™”’““““””“’“£®±“i(!$'*++*))'# !""%2MftŒ‡…„……‡ŠŒ‹‰ˆ‰ŠŠ‰ˆ…‚qYB2%%¢ ˜˜˜—•““”—™œªšp`K'3P}Œž™œŸ ¡¡¢£¢¡¢¤¤£¡ ¡¡¢¡¡¢£££¢£¤¤£¢££££¤£¡£¥¦¦¥£££¤¤¤¤¦¦¡¢£¢¢££¢ žžŸ Ÿžžžž™™šš——›ž–pcXRL77_`bccddc`\ZF0! &/:CMQOJIJKKFA>BC>4)%')(",>LOHC8+"%%&(++)&+7==82.''2:<==<;<?Lalla:(A]‚¦œ›š›œž žžžžŸŸŸŸŸŸŸŸŸŸ ¡¡ Ÿ ¡¡¡¡¡¡ ¡¢¢¢¡ ¡¡¡¡££¡¡¤¤£¢¢¡¢¢¡Ÿ ¢¡ ¡¡ ¡¡¡¡ §·º°šX!`Ÿª©µÃÀ±–}‰–£±²´¶´´»½µœ[ !!!""#"! 9‚¢ž•–”““’’““““’“““— ¬³¯¥‰h2##"#&(**))*($ !##&3Njzˆ…„ƒƒ…‰‹‹‰‡‡ˆŠŠˆƒoYE5%/”¤—–”•–•••–˜˜–£˜mP7!%?]„‘Ÿžš› ¡¡ Ÿœž¢¡¡¤¥¥£¢ ¡¢££¢¢¤¥¦¥¥¤¤¢¢££¢¢¤¦¥¤¥¦§¦¥¥¤¤¥¤¢¢¥¥¢¡¢¢£¥¥¤£ ž ¡¢ žœŸ ŸŸ›š››˜˜˜¢“€kbWRN8@U^aa_ZXK>2&%1;BGNQQPQRQPMGBCA;2*(+("8JRTQE7( "%$$&)+*(+3;@>5/*+49<><::<?Lakk^3.ET†¯µœ››œœœŸ¡ŸœŸŸžžŸŸŸ Ÿ ¡ Ÿž ¢¡ ¡¡¡¡¢¡ ¡¡¡¡¢¢¢££¡ ¢£¢¢¢¢¢¡¢¡¡¢¡¡¡¢¡¡ ¡¢¡¡¢¢¢¡ žŸ ¡£«©µ±vm““‚qU21_zŽŸ³¯¡j "!!#$$""##"!ƒ¬¦˜”•“’’’’’’‘’’’““”““š±²±³¬‡I'&$$&(+++-.+&" "$$(4Os€’Ž‡…ƒ‚…ˆŠ‹‰‡ˆˆŠŠˆ†‚t[G6%'?™¥‘’””––—™˜• —nI(,J^‰—¢¡™™¡¡ Ÿ››Ÿ£§¥¤££¡¡¥§¥¤££¤¤¤£££¢¡¡£¢¡ ¤¥¥¥¤¤¤¤¤¥¥¥¤¢¢££¡ ¡¢¤¦¦¥¤¤ Ÿ¡¡Ÿžž Ÿžœœœ˜——ŸhbXSP8@]XXPC(# ,8@EIOSUWZZWRMHA<6/+(*.&+@NVSH=1)%#"#&(**()-7@?70*+48;;:9:<>Lajie."4IK|±·œš›œœ›ž¡žœžŸžžŸŸŸ ¡¡¢¢ Ÿ ¢¢¡¡¡¡¡¢¡ ¡¢¢ Ÿ¡¢¢££¡ ¡¢¢¢¢¢¡¡¢¢¡¢¡¡¢£¢¡¡ ¡¡¡¡¢¡¡ŸžžŸ ¡¡¥±¼¯q!"""!$%$##$$#!G†¢ ‘“”’‘“’‘‘’’’“““““’’”“‘ ®®¦˜j*('&&(,./01-'# ! !#$)4Huƒ‰…ƒƒ…ˆŠŠ‰‡‡ˆŠŠ‰‰„{]G4#%[ ©’“••–—˜–\@-Qdš¡Ÿ››ŸŸžœ›š›ž¢¥¤¤£¤¤¤¥¦§¦££¤¤£¡¡¢¢¡¢¢¢¡¡¡£¥¦¥££¤££¥§§¦¤¤£¢¡ ¡¢¥¦¥¥¦¥¡Ÿ¡¡Ÿžœ›œžžœš˜—œ‘‚ecZTP9'<6/ "2>DHNUX\^_^ZTMD;2,('&(,$8KSRJB;3*$#$&(**(()0;<6/()38;:889;=K`iif("6LHw·ºš›››žŸ›œž ¡¡ ¡¢¢¢¢ žŸ¡¢¡¡¡ ¡¢¡Ÿ ¡£¢ Ÿ¡¡¢¢¢¢¡¡¡¡¢¢¢¢¡¡¢££¡¡¡¡¢¢¡ ¡¡ ŸžžžžžŸ £¯³®G !##"""$%%$#$%#!}¢Ÿ•’’’“‘‘‘‘““’‘‘’’‘‘‘Ž›°´¬›†T**('(,02222.'"!"! "#(3Eu‡ŽŒ‹‡ƒ„†ˆ‰‰‰ˆˆ‰Š‹Š†‚x]E1 2n ›‘‘‘“”••›—–„H:!2_y›¡œ›™œŸŸŸœžŸŸ ¤¤¥¤¤¥¥¦¦¤££¤£¢¡£¥££££¢¢£¤¥¦¤¢¢¤¤¤¥¦¦¦¥¤¤£¢¡¢£¤¤£¤¥¤ žŸŸžžžš›œ››œœ›˜˜’`e^WQ:#5BINTZ^``^\XQH9( "$&'#.BHJJD>7/'%%')))(&&)/21,%&19;:9:;<=J_jje! 4LJw»º˜œžž››œœœœœŸ Ÿžž ¢¢¡¡ žŸ¡¡¡¡ Ÿ¡¢¡ŸŸ¡¢¢ ¡¡¢¡¢¢¡¡¡¡¢¢£¢¡ ¡¡¡ ¡¡ Ÿ Ÿ ŸžžžŸŸŸžžžžž¢ªª‰. !!!"##"!"%&%$###"B„ ¢‘’“’‘‘‘’““’’““’‘‘ŽŽŽ’¤«©žw:*)(').23211-'" ! !! !(3Gw‹ŽŒŒˆ„„…†‡ˆ‰‰‰‰ŠŠŠŠt[C/N{›Ž‘“““”””“™•Ž}J1&6d„ ¡˜š›œŸ žœŸ ŸžŸ¢¤¤¤¤¤£¤¥¥¤¤¥¤££¥¦¥¤¤¥¥¤¤¥¥¥£ ¡£¤¤¤¤¥¤£££¤££¤¤¤£¢£¤£ žžžžžžœœœœœœœ›˜•™of`XO;,@MQW[^_`^ZVOC5$#&&"#5=CGB;7/)%%&'(*+)%"!"%%"$.8;9:<=<<J`lkb/I?o¸·›œŸž››œžžžŸ ŸžžŸ¡¡ ¡¡ ¢¢ ŸŸ ¡ ¡¡¡¡¡¡¡¡¢¢¡¡¡¡¢¢¡¢¢¡ŸžŸ ŸŸ ¢ Ÿ ¢¡Ÿ ¡ ŸŸŸ ŸŸŸŸŸžŸ¦®«ŽZ!$'&$$$##"!"$&%$"!!0„¡¥š““”“‘‘‘‘‘‘’“““““‘Ž‘š©ª¢’\)('')-120.-+'#! !! !"!!!"(4Ht†Œ‹‡………‡ˆ‰ŠŠŠŠŠ‰ˆ}mXB-f†—“‘“””••”“”€rE(%:k‡œš—ššœŸ ž›œ ¢¢ Ÿ ¢¢¡¡¢¢¢¢££££¤¤££¥¦¥££¤££££¤¤ŸŸŸŸ£¤¤¤¤£¢¢££¤¦¦¥£¢¢£¢¡ žžžžŸŸœ››š›››š—“…sic[P="2EPTY]^][XTN?) %(($(7DLE:4-(&$%&).30'!,799;=>=<Kblka*G>r··™žžžœœŸŸ Ÿžž ŸŸ ¡¡ ¡¢¡¡¢¡ Ÿžž ¡¡¡¡¢¡ ¡¡¡ ¡¡¡ ¢¢¡ ¡¢¢ ŸŸŸ ŸŸ¡£¡Ÿ¡££ Ÿ ŸŸŸŸŸŸŸžžŸŸ «°ª]"%(+($%%$$#"#%&&$#! l“ž¡—•”“’’‘’“’‘’’’““”““’‘ŽŽŽŽš«®¨Ÿ-)&%&),-,*)(%#! !"#!""! """'1Dl|ŒŒ‰‡†…†‡ˆ‰‹‹ŠŠˆ†yjVA.mŸŸ”““““”––”’ˆze=##;\œœŸœœž ¡ œœŸ¢¢¡¡¢¡ žŸ¡¡ ¡¢¢£¤¤£££¥¥£¢££ ž¡¥¥¢ ¢¤¦¦¥¤£££££¥§¨¦£¡¤¥£¡ ŸŸžžŸ›™™˜™››™—•”‹…mlg_S>$6HRVZ\\XSOG<*"'*+*%"! 7KUL;3,('%#$(/88. .789=>>>>Ndlia)@>y·ºžžžžžžžŸ ¡Ÿž žŸ ¡ ¡¡¡¡¡¡ ŸŸ ¢£¢¢¢¡ ¡¢¡¡¡ ¡¡¢¢¢ ¡¡¢¢¢ ¡¢¡Ÿ¡££¡Ÿ ŸŸŸžŸŸžŸ¡¡ ¡¥¦§ž|"&()+'$%%&'('&&&%$#" :’«©–•–•“’’“““’‘’’’’’’’‘ŽŽŽŒŽ•—›1+%#"#%&&&$"! !#"!""! !##"&0Idv‹ŒŒ‹‰‡‡††‡‡‰ŠŠ‰‰ˆ…}mXA/n–£˜•••“‘“––•‘‡…tT,+Jdˆ–ž¥¤ŸžœŸ¢¡žž¡££££¢¡ ¡ ¡¡¢££¤£££¢¤¥¢¡¢¢¡Ÿž¥¦¤£££¤¥¦¦¤££¤¤£¥§§¥Ÿ £¦£¡¡¢¡œœžŸžœš™˜™š›˜——˜“ˆoolfX@ 2EOTVWTOG;*#&(+--./%5LXO:1+)*("!'1:=5% 188<@><=@Qglh^)HOz¶»ŸœœœœžŸŸžž ŸžžŸ ¡¡ ¢£¡ ¢£¢¡¡¡ ¡¡¢¡ ¡¡¡ ¡ ¡ ¡¢£¢ ¡¢¢¢¢Ÿ ¢¢¡ŸŸ¡¡ ŸžžžžŸ ŸžŸª°¤x$ "%''(('%%%&*+)&%$#""!l—š ¡““”•–“’“““’‘’’’’‘’’’’‘‘‘‘‘ŽŠ‰0+$ !" "##"!""""!"!!%1Hbu‰ŠŒŒˆ††††‡‡‡‡‡††‡‡‚sY@/s™£”•––•••”Ž‡cC 2MW„–¡§ŸŸžŸ¡¡ ¡£¤¤££¢¡ ¡¢¢¢££¤¤¤¤¤¤¤£¢¢¢£¡ ¥¦¥¥¤£¡¡¤¦¥¤¤¤£¢¤¦¦¤žŸ£¥£ŸŸ¢¥ žŸžœ›››ššš˜—˜˜‹ynpj\B"*ALNNMH>3$#$%*-.170(?KF70-+.+" '2>?7&#!$289>@>=>ASgmh\+HNy´µšœœœœœ›œŸ ŸžŸ ¡¡ ¡¢¢¡ ¡¢¢¡¡¡ ¡¡¡¡¢¡¡ Ÿ ¡¢¢ ¡¢¡ ŸŸ ¢¢¢¢¡¡¡¡¢ Ÿ ¡ ŸžžŸŸŸ ŸŸžžŸ¦±³ªŠ5!&)((((&%%$$()'%$"!Nž²¯Ÿ””“”•–”“’““’‘’“’‘’’’‘Ž‘ŽŒ‹Š/+$ !""$&'&$$##$%#"! %3Ic|‹Œˆ……†ˆ‰‰‡„„ƒ„†„ƒyXA/v›¤“”––‹””“–•R7!.R_‡š¡žžž ¡¢££¢¢¢¢¡¡¡ ŸžŸ££¢¢¢¢¡ ¢£¤£££¢¢££¢£¤¤¤£¢¢¢¤¥¥¤¤¤¢¢¤¥¤¢ŸŸ¡£¢ŸŸ£§¦¤ Ÿœœœž›šš˜˜™š‰ˆ€usn^E%6CDC=/$$$%),-186(,:;88511-"%2@A6$&)+279=>>?@CSflg\,G:s³±˜›œœœžœ›œœœœžŸ ¡ žŸ ¡ Ÿ ¡¡¡¡¡ ¡¡¡¡¢¡ŸžŸ Ÿ ¡¡¢¡¡ Ÿžž ¡¢¢¢¢¢¡ ¡ ŸŸŸŸŸžžŸžžžŸŸ¡®±¨…B# !%'&&&&%$#"#$#""#!eŽ¢«¨”••”“”••”“’’’’’’’’‘‘‘‘’’‘Ž‘‘ŒŒŒŠ/,% !"%()*,-*(&%$%%#!%2Gf€‹‹Šˆ‡ˆŠ‹Œ‹‡„‚„ƒ{YA,|¢“”–’Œ““”™uC,'7\v”—›œœœž ¡¢¢¢ Ÿž žœœ¢¢¢£¢¡¡ž›–£¤¤¤¤££¤¤¤¥¥¥¥£¢£¤££¤¤¥¥£¢£¤£¢¡¡¡¢¡ž¡§¥££¡Ÿ›œžžœ›š˜˜œ™‡„€zun^D%".1.% %%&)-/3882#"26;A;43.$!%1CC3#(/26:<<;<>@CRejdX.I:u¼´˜œœœŸŸžžžžžžžžž ¡ žŸ ¡ ŸžžŸ Ÿ ¡ ¡¡ ¡¡ ¡¡¡¡ žŸŸŸ ¡¡¢¢¡ŸžŸŸžŸ ¡¡¢¢¢¡Ÿ Ÿžžžžœœœžžœž§±²šG !"##$%$"""""! !A°¬›””••“’“”““’‘‘‘’“’’‘Ž‘‘‘‘‘ŒŒ‹ˆ/,%!""#&(+/24540,)'%%$$"!!'0Ek‚‹‰ˆˆ‡ˆ‹Ž‹‡ƒ€{U>*„£ž“”•’ŽŽ“’“–‡m7&/Fm†ž“™žœ›ŸžžžŸŸŸŸŸžžŸŸœœžŸŸ ¡¡¡¡Ÿ¢¨¥£££¢¢££¥¦¥¥¤¢¡¢¤¤¤¤¥§¦¤£¡¡¢£¤£¢¢£¥§ª¤¡¡¡ žžžŸžœ›™˜˜›œŠ„|xq^E$ %'(*.0479:-)2;@933-%"$.>=*,488;><:;=ACQdh_K0J2zÄ»š›››œžžŸŸŸžŸŸŸžžŸ ŸŸŸŸ ŸŸŸ ¡¡ ¡ Ÿ ¡¡ŸŸ ŸŸžžžŸ ¡¡ ¡¡¡ŸžŸ ŸžŸ ¡¢¢¢ Ÿ ¡ ŸžžŸžœœžžžœ›œœœœœž¬¶§y1 !"""!!"#"! V“˜œ’“““”””“””’’’‘‘’“’’‘‘‘‘Ž’’ŽŒŒ‹‰.+$#%'(*,.05:>@?:62.)&%%%$#$)2Fj…‹ˆ‡‡†‡‹ŽŠ‡€€„{U=)©™‘’“‘’”’•”`4 0K{£ž”•žž›šœžžžžžŸžŸŸŸŸžžžœ›œž ¡¡¡¢¥¥¥£¢¢¡¡¢£¥¥¥¤££¢£¥¦¥¥¥¦¤££¢¢£¥¥¤¡¡¤¦§©ŸœŸ¡¡¡ Ÿžœœœš™˜›š‡„‚}{scJ( %&'*-037;>6!+9>5/1,%"#*74 '19>=;;:;;=ABPcfZ?1J1tÁº˜š›œžžžŸŸŸŸŸžŸ Ÿ ŸžŸ ¡¡ Ÿ ¡¢¢¡¡¢¡ ¡¡ ŸŸŸŸŸ ŸŸ ¡¢¡ Ÿ ŸžŸ¡ ŸŸ ¡¢¢ žŸ ¡¡ ŸžœžŸžžœ›œœœœžŸ©ž†* !!! 0†ž¡¥—•”“’’““’’’’’’‘‘’’‘‘‘ŽŽŽŽ‘ŽŽŽŽŽŽŽŽŒŠŠŠ‰+)$$(+.2567<DJLKIC=81+'%%%%%%)4JjŠˆˆ‡††ˆ‹Œ‹‡‚€‚€€qP<)“§‘‘‘“•–—–ŒvS'%9Z…•¤¡™Ÿžšœ ŸŸžŸ ŸžžŸŸŸŸŸŸžžŸ Ÿ ¡¡ ¡¢¢£¢¢¡ Ÿ ¢£¢¢£¤¥¤¤§§¥¡¢¥¤¢£££¤¦¦¥¡¢¤¥£¢šž¡¡¡¡ žœš››™˜—”†‡†€€jO. #$'),/26<@=/ 4:0-30&##'/* +158;=;8:>>>?@PceW82J3m»¸˜šœžžŸ Ÿž ŸžžŸ¡¢ Ÿ ¡¢¡¡¡£¢¡ ¡¡¡ ŸŸŸŸ ¡¡ ¡¡¡ ŸŸŸ ¡¡ ¡¢¡ŸžŸŸ ¡¡ Ÿžžžœœœ§¨†3 ! !# ¢–’”””““““““’’’“““’‘‘‘‘’’‘ŽŽŽŽŽ‹ŒŒ‹('# %+048<??AIU[ZUNHB<6/)%$####'3Kgy„ˆ‰‰‡†ˆŠ‹‰…‚‚‚ƒ…{gK:&–ŸŒŽ‘”——œ€hG+DcŒ—¡ Ÿ¡¡š›Ÿ ¡¡ ¡¡¡žžŸ Ÿ ¡ ¡¡¢£¤£¡¡¡¡¡¡¢¢¡ Ÿ ¢¤£££¥¦¦¦§¤ ¡¢¥¦££¤¤¥¥¦¥¥¥¤£¢¡žœž¡¡ Ÿœ›ššœœš˜–’‹ˆƒ„ˆmP4"#')+/38=@A;)/7,.:8-(&(.%(575238:8:=?>>AQceV32KDk·¶œœŸŸŸ¡¡ žžžŸ ¡¡ žžžž ¡ŸžŸ¡¡¡¡¢££¡¡¡¡ ¡¡¡ ¡¡ ¡¡¡¡ ¡¡¡¡¡¡¢¡¡¡ ŸŸŸŸŸ ¡¡ ¡ žžŸŸœœžžœ›œœœ›ž®±¯¢e p©¯¥”’“’’’’““’’“‘‘’””‘ŽŽ‘‘‘‘‘‘ŒŽŽŒŒŒŒŒ‹‹‹ŽŒŠ%# "'.59=BHLJHP`fcZOHC=71+&$##"!%0Hf|„‡ˆˆ‡†‡ˆ‰ˆ†……„„„†‰~gP8"2™›ŒŽ‘”—Ÿš†sU;)Rl‘˜š›ž žšššŸ¢¢¢¡¢¡ŸŸ ŸŸžŸ¡¡¡¡¢£¤¤£¢¡¡¡¡¢¢¢ Ÿ¡£¤¥¤¤¥¦§¨ª¦£¡ž§§¤££¥¦¤£¤¥¥¤£¢ žžŸ¢¢¡ žœ›œ››š™˜™—ŒŠ‡‡ŠkU9"#%(+.18=@BC4.7-0@>30--0$)761**2878:<=?BRceS.2LHmµ¶žžžžžŸ ŸžžŸŸ ¡¡ žžŸŸŸŸŸŸ ¡¢¢¡¡¡ ¡¢¢¡¡¢¢ ¡¡ ¡¡¡¡¡¡¡¡ ŸžžŸŸžž ¡ ¡¡ žŸž›œ›™™›¤¦³ªcVˆž‘‘““’‘‘’“””’’““’““””’‘‘‘‘‘’’ŽŽŽŒŒŒŒŒŽŒ#!$)/7>AEKQURMUflh]MD@;72+'$"""!$.Fhƒ††††††††‡ˆ‡ˆˆ†…„†Š‚jR6$E›Œ‘’’’•–¡”tbK+$7Zx–ššž ›™›¢¢¢¢¢¢¡¡ ŸžŸŸ¡¢£¤¤¤¤¤¤£¤¤£¢¤¤££¤¥¥¦¦¦¦¦§©ª¨¦¢¢¥§¤¢¢¥¦¤¢£¤¤££¢ ŸŸ ¢£¢¢¢žœœœœ›™™š–‹‰ˆ…‰nV<!"$%'*-.19@BDF="*4.3?=760-/&'34-"$3;;::;>AESdcN'2KFk±´œœžŸŸŸŸŸŸŸŸŸ Ÿž ¡ŸžŸ ŸŸ ¡¢£¢ ¡££¡ Ÿ ¡¡¡ ŸŸ žŸŸžžŸŸŸŸžŸŸœžŸŸŸžœœžžœ›››™™››Ÿ«º°p@Œ¯ª’‘’“”“‘‘’““’‘‘’““”””””“‘‘’‘ŽŽŽŽŽŽŒ‹‹ŒŒŽŽŒ%$! #)09ELLNSWXRKP`fcXI@<972+&##$$$'/FgŒ†…†………†ˆ‰‰‰ˆ†„ƒ„†‚mT5+MŸ£’““““––žŽc[@%-Hmšœ˜™˜™›œœ›œ ¡ ¡¡¡ŸžŸ£¥¤ Ÿ ¢£¥¤¤¤¤¥§«ª¢¢¥¥¥¤¥¦¦¦¦¦¦¤¤ª©§¥¤¤¤£¢¡¡¢¥¥££££¢£¢¡ ¡£¤¤¤¢Ÿœœž›š™–Œ‰‡†ŒsZA&#%%(+,-18?DFGC1"--29888.)-*"*-&"5>?=<=?CGVebE#2KHk±·œœ››œžžžžŸ ŸŸŸ Ÿž žŸ¢¢¡ ŸŸž ¢£¢¡Ÿ¡££¡ ¡¡¡ ŸŸŸ ¡ ŸŸŸŸžœŸŸžžžžžžŸŸ Ÿœœœššš›››œœœ¬¯¥p…¡§£’’’’“”“’’”””“’““””””“““’‘‘’ŽŽŽŽŒŒŽŽ)'%#"#%,6CPYYY[][TJKTYXOD=:983+%""###(1FdwŠ‡‡†…„…‡‰Š‰ˆ‡†„ƒƒ‚oS43W ‘”””“’”—Ÿ’fU75Rn‚¢£˜™˜™š›œž ¡¡Ÿ ¡¡ŸžžŸ¡¤¨£ ¡£¤¤¤¥¥¦§ª¤¡¢¤¥¦¥¥¦¨§¦¦¦¤¤¦¦¥¥¥¥¤¡¡¡¡£¦§¤£¢££££¢¡¢£¥¥¥¤ œžŸž›™–’Žˆ‡Š”t^G,*C?!#$%(),/3:AGKKI@)"(.3368-'..#"%!#4<=<=?CFKZhcC!0JIu¸»ššššœžžžŸŸŸŸŸžŸ ¡ žžŸ ŸžŸ££¡¡¢¢¢¢¡ ¡¢£¢¡ ¡¢¢¡ ¡¢¡ ¡¢¡ŸŸ ¡¡ ¡¢¢¡ Ÿ ŸžŸ ŸžžžŸŸŸžŸŸŸœœ›š™™š›œœœŸ¢ªopž¬£”•”“’“”’‘‘“““’’‘’““““’‘‘ŽŽŽŽŽŽŽŒŒŒŒŽŒŒ-+(%#$&-:HTYYXY[YRGDGJKF;55673+%##$$#(2Ecv‰‡ˆ‡…„†ˆŠŠ‰‡†‡‡†„ƒlP1;a› ‘”–•“‘‘’š’cK-#6Xo‰§ ™™™›œœœžŸŸŸžžŸŸ ¢££ ŸŸ¡¢¢£¤¦¦¥£¡¡£¢¢¤¥¢¡¥§¦¥¥¤¤¥¥¤££¤¤£££¡¢¥¦¤£¡¢¤¤¤£¢£¥¥¤¥¤¢žžŸŸžžžš˜–”’‹™xeN1wtZ"#%&&&)-39@GKKKI3 '-078-(--& &38779<AEL[id@ .IKw»¼—™ššžžŸžžžžž ¡ ŸŸŸŸžžŸ¢¢¡¡¢¢££¢¢¢¢¡¡¢¢¡¡¡¡ ŸŸŸ ¡ Ÿ Ÿ ¢¡ŸŸ¡¢¢ŸŸ¢¤¢¡ ŸŸŸžŸ Ÿžœžžžœœš™™™˜™›œ›œœœ¨±§ps¡’’“””“’“’‘‘‘’“’’‘‘‘’‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŒ‹ŒŽŽŒ1.*'%%'/=KQSSSSSSNE?=<=9.)+041*%#$%%%)1Fcv††ˆˆ†…ˆŠ‹‹Šˆˆ‰ˆ‡„…ydJ.6h› ’––”’’”–\<*E_w£žš››œžžžžŸžœššœžžœ¡£¢¡ ŸžžžŸ¡¢¤¥¥¤¡ ¢£¡¡£££Ÿ£¦§¦¥¤¤¥¥¥¥¥¥¥¥¥¤£¢¤¦¦¤¢¢¤¥¥¤££¤¤£££¤ Ÿ Ÿž›˜–—–•‘Ž’™ƒoS5†J#$%#!"'-6?EJMNM:"'0==1.1.*&! "*375569=CKYhb80ICq¹»ššššœœœžŸ ŸžžŸŸ Ÿž ¢¡¡¢¢££¢¢¢¡¡ ¡¡¡¢¢¢ žŸ žŸ¡¡ Ÿ ¡ŸŸ¢¤¢ ŸŸžžžžžœœ›œžœ›šœœš™™š™™š››››œ ª²©h`–ª®’’’’’““’’‘‘‘‘’’‘‘‘‘‘ŒŒŒŽŽŽ‹‹ŽŒ‹ŠŠ‹ŒŒŒ‹Œ3/*'%'*1<FIJJHGHIF=6224/&"%+0.)%$%&&')0Ectƒˆ††…†‰‹‹ŒŠŠ‹‹Š‡ƒ‚u^E-:p™—Ž‘”•””——‡S.2Teƒ”›™š›››œœžžš™™žžœ ¡¡¡ ŸŸžš™¡¢£¢¢¢ ¢¢ ¢££¤¤¥¦¥¤¤¥¥¥¥¥§¦££¤¤££¤¦§¤¢£¤¤¥¤¡¡£££¢¢¡ŸŸ Ÿžœ—–—˜—–’†vW6"Lkª•#$# '3>CJOQM= !,@A3163/.-(#"$*166568:@IUd_/4NDn··™š››œœ›œœž ¡ ŸŸŸœžŸ ŸŸ ¢¢¢¢¢££¢¡¡¡ ¡¢¢¢¡ žŸ Ÿ ¡ŸŸ¢¢¡ŸŸŸžžŸ¡¢¡ ŸŸŸžžŸŸžœœžžœš™›œœœ›šššš›œ›š™šœœ¦¬ c$cœœ˜“•’‘‘‘‘“’’““’’’“’‘’’‘‘‘‘‘‘‘‘‘‘ŒŽŽŽŽŽŽ‹ŒŽŒ‹Œ‹ŒŒŒ2.)%$(-27<>>;9:<><3+)+-)" #(,+(%$%%$$&-B`uƒ„……ƒ„‰ŠŠŠ‰‰‹ŒŠ†‚u_D,L{”““”••£’€zF%!8XiŒŸ¡œ›œœ›œœžœš—œ ŸžžžŸ ¢¢¢¢¢¡¢¢¡ ¡¢¤£¡ ¢£¤¤¤££££¤¥¥¥¥¥¦¦£¢£¤¤¤¥¦¦¥¤¥¤¤¥¥¡ ¢¢¢¡¡ Ÿ œœ™–—˜˜–‘Œ]>({”¥™ $$#$5AEKTUN?%+DG66A?611.'"$*165568:?HSb]+:U<o·³˜›œœ›œžžŸ¡¡¡¡¡ žžžžŸŸŸ ¡¢¢££¢¢¢¢¡¡¡¡ ¡¡¡ ¡¡¡¡¡¡ Ÿ¡¡ ¡¡¡ ŸŸŸ ¡ ŸŸ ŸŸ Ÿžžžžžœš™›œœ›šššœ›™™˜™›œœ¤³²†K•¨—‘””’’‘‘’’’’‘’“’‘‘‘‘’‘‘‘’’’‘ŽŽŽŒŒŒ‹ŒŒ‹ŒŒŒ‹‹‹Œ.+'$#(.000/*''+163*%$'*'#"$')*(&%%%$#$+?^sƒ„†…„†ˆ‡‡‡††ˆŠŠ†ycE)Xz‹Ž‘“•–™›ˆqjA$&3]s¢¤œ›œœžžœœž¢¡ ŸŸžžŸ¡£¤£¢¡¢¦£¢¡ ¢¤§¦¢¡¢¤¤£¢Ÿ¡¢£¤¤¤¥¥¥¤¤£¢£¤¤¤¤¤¥¦¦¦¥¥¥¤¢¡¡¢¡¡¡¡¡¡¡¡Ÿžž›˜—–––•–“”…aH1Eƒ•‡‰™ "$$5DFKX[TD-'@C7>OOA41/)%',255579:>GTaZ#;WL}»¶™œœœžžœŸŸžžŸ ¡¡¡¡ ŸŸŸžžŸ ¡¢¢¡¡¢¢¢¢¡¡ žž ¡¡¡ ¡ Ÿ ŸžŸ Ÿ ¡ Ÿ ¡¢ žžžœš™›œ›››š™››˜˜™š›ššš¨¶³‘`!D†–ž˜’’““”“’’’’’’‘’“”’’’“““’‘’’’‘‘’‘ŽŒŒŒŒŒŒŒŒŽŽŒŒŒ)'$#$(-.,(""(-+%!!$'&$$&'(('%$$%%#%,@_u…†ˆˆˆˆ…„„„„…‡‡…}vhF&]~‘“‘’””–•„ma3 /Ak€‘ŸŸœœŸ ¡ŸžŸ¡ ŸŸžŸŸ ¢£¢¡¢£§¤ ¡¡¡¢¥¨§¤¢¢£¤£¢¡¢¤££££¤¥¥¤££¤¤¦¦¥£¢¤¦§§¦¥¤£¢¢¢££¢¢££¡¡¢ Ÿš˜—–•˜š”Š‚lU4&¦…™•!#&3EHN[`[K5'"8=5@VZK:30*'*/355568:>GTac5PW‚µµ›žžŸžŸŸ¡¡ ¡¡¡¡¡¡ŸŸ Ÿžž ¡¢£¢¢¢£¤£¢¡ ŸŸŸ ¡¡¡¡¢¡ ¡¢ ŸŸŸŸŸ ¡ ŸŸ Ÿ ¢£ žŸ žœœœœ›šœž››››šš›œ›ššššš™š››š¥«§‚@.ˆ¦ª¤™’‘‘’’’’”””“‘‘“““‘‘’“““‘‘Ž‘’ŽŽŽŽŒ‹‹Œ‹ŽŽŒŒŽŽŒ‹‹$"!#&)..*$"''" !$''&'))('%$"#$%#%.Ddy„…‡‰‹Šˆ†………„„…†„‚ueD"kŽ“‘’˜“€jW%#6Sy‡ššžœœ ¤ žžŸžžžžœž£¡žžŸ ¡¢¡ŸŸ ¢¢¢¢£¤¥¥¢ ¡¢£¢¢¤¥¤£¢¡¢££££¤¥¥¦§§¤¡£¤¥¥¤£££¢¢£¤¥¤£¤¤¢ Ÿ Ÿœ›šš˜˜šš š}wcB/kˆ•“‡†Ÿ #&+?GMX_^R<2.074?U]UD7/(',13566679>HU``,A]ˆ²¹ žœ›žŸ Ÿž ¡¢¡¡ ŸŸ¡¢¢¤¤¢¢£¤¤¢ ¡¡ ŸŸ ¡¡¡¡¡¡¢£¡¡¡¡ ŸŸŸžŸ ¡ŸžŸ ŸŸŸ ¡ Ÿ žœœ›œœœœ››››šššš›ššš™™™šššš™ «¶¶°ªžiKl}‹–ž¤£¢¤£“‘’“’““’’”••“‘‘’““’‘‘’“’‘‘‘ŽŽ‘’ŽŽŽŽŒŽŽŒŽŒ‹Š! "&*//) $%#"#%())++)('&$##$$#&0Fg{„„‡‰‹Šˆ†…………††……„ŠubA ™ Ž‘¢~gK(A[zŒ™ž˜œœ™ ¢ŸžŸ Ÿžž ¢¢žž ¡ Ÿ¡£¢¢ Ÿ¡¢¡¡¢£¢¢¤¥¢Ÿ ¡¢¢¢¤¤¤¤£¡£¥¤£¤¥¥¦§©«¦££¤¤£¡¡£¤££¤¤¤¤£££¢ ¡žš›œ œ£¨ƒsN/*IŒ£—†‡ˆ¡~""#$2@JRX[S?88'-63>U_]P;,%%.34567778?JT^\&7]“¹½œš›œžœ›œžŸŸžŸŸ ¡¡¡¡ ¡¢¡£¥£¢£££¡¡¡¢¢¡ ŸŸŸŸ ¡¡¡¡¢¡ ¡ žž žžžžžžžžžŸŸŸžœ››ž›š›››››ššš™š›™™™™™™˜™™˜——Ÿª°¹¾¾±x-Uu‡Œ—¨²µ³°®²² š“”““’‘‘‘‘“”“‘‘’‘‘‘’’‘‘ŽŽŽŽŽŽŽŒŒŽŽŒŽŽŒŒŽŽŒ‹Œ‹Šˆ‡ !"%)//) #&&&&'*-./,)''&$$$$#"'0Ddxƒƒ†ˆ‰ˆˆ†„……†‡‡†……ˆr^>ˆ”ŒŽ‘™ƒtZ;0N^x ¢œœ™’”žŸ œžŸ ¡ žžŸ Ÿ £§¤¤¡ £¤¡ ¢£¢¢£¤¢ ¢¢¢¢¢¢¤¥¤¤¨¦¥¤££¤¥¦¨¨¨¤¥¥£¢¢¢£££¤¥¥¤¤¤¤£¤££¤£¡œž žœ¦™Š…VA7.'$# l’ˆ†‡‡‡›s&$$$!!6DMRUP?8:++53>T`cZB.$#+01237998>LU]Z#5VŒ¸¸–˜š›žžœŸŸŸŸžžŸŸŸ ŸŸ ¡¡¡¢£¢¢¢¢¢¡¡ ¡¡ ŸŸŸŸŸžžŸ ¡¡ ŸŸ ŸžŸœœžžžžœ›š™ššš›››š™™šš™™™™˜˜˜˜—––˜—––—•›®¯t&A[o~†Ž— ¥§«¯±°ª¤¥©£œ’’‘’““’‘‘’’‘‘‘‘’’‘‘’’“‘‘‘‘“‘‘‘’ŽŽŽŽ‹ŒŽŒŒŽŒŽŽŽŽŽŽ‹ŒŒ‹‹‹Š‰! $)./*"$)++,-0232.(%%%$$$"!"(0Dcy†„†‡‡‡‡†……………………„…p[<‰œ‘ŽŽŒŒ{jN+%5\h—¦§ ¡žš—˜œžžž››ŸŸžŸŸŸŸŸžŸ¡¡¤¤¥£¡¤¥¢Ÿ ¡¢¡¢£££¤¥¤££££¥¥¥¤¦¦¥¤£¢£¥§§§¦¥¦¦¤££¤£¢£¤¤¤£¤¥¤£¤¤¤¥¤¢ŸŸ ¡¡ž›šœŽŽnYK;/*($jŸ¦ Š‡Š‹Š™i(''''$-AMQQK=:<.'22=S`faK4'$*.//048:8>LV\J":Y‡µµ—šœžŸŸŸžžžžŸ žžŸŸžžŸ ¡¡¡¡¢¡¡¡¡¢¢¢¡¡ŸžŸ žŸ¡ ŸžžžŸ¡¡ ¡¡ žŸ ŸžžžžžžŸžœ››œœœžžžžžžžœšš™šš››š™™™™™™š™—˜™˜—–•—–•–——–›®°H Wq‚’Ÿ¤¥§«¯°®±´±§©ª¡‘‘‘‘‘‘’“’‘“’‘‘‘Ž‘’‘’’‘‘‘“’‘’’ŽŽŽŠ‹ŽŽ‹‹ŽŽŒŒŽŽŽŽŽŽŒ‹ŒŽŽ‹‰Š‹‹‹$" #(./+&"(.22259984.'"!"#$#""$(0Ebw†„„ƒ…‡ˆ‡†……„ƒ‚‚ƒƒ„oW9†—’ŽŠweE(-Biv‰œª¥ ž›˜˜œŸ žœ›ššžŸŸžžŸŸžž ›¡¢¢¡¤¥£¡Ÿ¡£¢ ¡£¤¥¥¤¤¤££¤¥£¢£¤¤¤¤¢¡¡£¦¥¤¥§¦¤£££££¤£¢¢¢¤¥¤£¢¢£¤¤£¢¢¢¡¢¡œš™š•’‘{i`M;1/* Pui='!!Ac}‡™”ˆˆŠŠ‹Œ‹•–h'()))*&$9JOLE;9;3'*0>S`fcQ;,(+./..05::>JUZH#6]Ž¶¶›œžŸŸŸžžŸžŸ¡¡ ¡ žž ¢¢¢¢£¤¢ ¡¢££¢¡ŸžžžžŸ ŸŸ ¡¢¢¡¡¡¡Ÿžž ŸžžŸŸžŸ ŸœŸžœžŸžžžŸžœ›š™™™™™™™™™˜˜™™™—˜š™˜—–——••˜™˜—ž±³K' `„™¦§§¬®®²³§žž—‘‘‘‘’““‘‘‘‘‘’“’‘‘’’’‘‘Ž‘“’‘’‘‘’’’’‘ŽŽŽŽŽŒŽŒŽŽŽŽŽŽ‹ŠŠŠŠ‰%#"!$)/21+% !'/5778;=<94-&!!#####$'0Hcs„‚ƒ†‡†……„…„‚‚ƒƒ‡pV8†–“”’’‘‘’ƒkX9!!3Il•œ›ŸŸ Ÿž™˜› ¡ŸšœœœŸŸŸŸ ¡ žŸ¡ œžŸ ¡¢££¡ ¡¤£¡ ¢£¤¤¤¤¤£¢££¢ ¡¤¥¦¦£¡¡¡©§¤¤¦¦¥¤££¤¤¥¤£¢¢£¤¤¤£¢¤¤¤££¢¢¢¢ š››Ÿš’Žnl_J>;5(2`Ÿ¬Ÿš“‘œ¤¨¥˜‡…‡ŠŒŒ‹Š‹“Œb&&'(*-/+*BJFA:7740&.@S`fbSA1+-//../4;>@JTXE"/Z”º´ŸžžŸ ŸŸ ¡¡¢¢¢¡ ¡££££¥¥¢ ¢£¤£¢¡ŸžžŸ ¡¢¡¡¡¡¡¢¢¡¡¡¡ŸžŸ ŸžŸ ŸžŸŸžžŸŸžœœœžœœœš˜——˜˜™™š™˜™™™˜—˜š›š˜—˜—•”—™™™™›ª§ŸD gŒ¤°²¯ –•”‘’’‘‘’’’’’’’‘‘‘’‘‘’’‘‘‘‘‘‘‘‘‘‘’‘‘“‘‘’’‘‘‘Ž‹ŽŽŽŒŒŒŒ‹ŒŽŽŽŽŒŒŒŒŒŒŠŠŠ‰ˆ‡‡&%$#&*2884,'$#%-689:99:84/+'" !#""""#&0Hdv‚„ƒ‚ƒ†‡……„ƒ„…„ƒ„‡‚oY7(‹š–’’’’—”€^J+#9Plƒ¢¦™›žŸŸš˜š ¢¢œž››œžžž¡¥¥¡ž ¡ ŸžžŸ Ÿ ¢¤¤¢ ¡££££¤£¢¢¢¡¡£¥¥¥¤¢ ¡¤«¨¤¤¥¦¦¤£££¥¦¥¥¤££££¤¤£¤¤£££¢¡¢£¢žŸ œ•’…uujNIJC0#?f£¬š¢¥¨¨•Œ†……†‡‰‹Œ‹Š‹šƒX%$%(+19:.7D?:63214./BP]d_QA2---....29=@IQTA"1R¹®ŸžžžžŸ¡¢¢¡ ¡¢¡ ¡¢¢¡¡¢¢¡¢£££¥¥¢¡¢££¢¡¡¡ ŸŸŸžžŸ ¡¢¡¡¡¡¢¢¡ ¡¡ žžŸžžŸžžžžžžžŸžœ›››œœœœœ›™˜˜™˜˜™šš™š™™™™™›œ›™˜˜—–•—˜™˜—––¨±¯Ÿo&h—¯®©ª£™“‘‘‘‘‘’’’’’’‘‘’’’’’’’‘’”“‘‘’“’‘‘‘’‘’‘‘““‘‘’’‘‘‘‹ŽŒŒŒŒŒŒŒŒ‹‹ŒŒŠ‹ŒŒŒ‹Š‰ˆˆ‰$#""&,6=@<5/,*,29;=>:731.*(&#!!! $.D`tƒ…††††………ƒƒ„„„ƒ‚ykY4 -‰›”’’““™}T<% ';Zu ¥™™šŸžœšœŸ¡¢›š›œ› ©¤ž›ž ¡¡Ÿž ¡Ÿž ¡¡£¤¢ Ÿ ¢£¢¡£¥£¡£¤¤¥¥¥¤¢¢¢£¥ª¨¦¥¥¦¦¥£¢£¤¥¥¥¥¥¤££¤¥¥¦¥¤£¤£¢¢£¢¡¡¡™•’†{…|USWQ:)Jp¡”ŒŽŽŒ‹ŒŒ‰ˆˆˆ‡ˆŒŒ‹‹P '%$'-6?D<&+<:421..66"1DNY`]PA4-+*+./147:>IQR;#6SŠµ®žŸ Ÿ ¡££¡¡£¢¡¡¡¢¢¡¡££¢¢¢¢£¤£¢¡££¢¡ ¡¢ Ÿ ¡ ¢¢¢¢¡ ¡¡ ¡¡ žž ŸŸŸŸŸžžžžžžœž Ÿ››œœœœœ›››šššš™˜˜š››™™˜˜™š™™šš™˜˜˜—–—˜˜—–••–œ ²§š‘‹yƒ•§¸½¹¢š–•––•”“’‘‘’““”””’‘‘’’’“““‘‘“”’‘‘’’‘‘ŽŽ‘’’“’‘“”“‘‘‘‘’‘‘’’‘ŒŽŒŒŒŒŒŒŒ‹‹ŒŒŠ‹Œ‹‰ŠŒŒŠŒŒŠŠ‰Š‹ $,7>BA;75557:>@@;4.,+(%$#""!#,@Zq|‚‡ˆ‡†††‡†„„„……‚~xjS0!)†œ–’’’–š†uS0 !!.IfƒƒœŸš˜˜›žœš›œ››œœ›››š—— ›”œŸ¢¢ŸŸ¡¡ žŸ ¡¡ ¢¢ ¡££¡ £¤¢¢¤¥¦¦¥¥£¢££££¥¦¦¥¤¥¦¦¤¢£¤¤££¤¥¤££¢¤¦§§¥¤¤£¢¢¢ Ÿ œ™š˜‡•ˆAY`[?!$Lm Ž‹Ž‹ŒŒŠŠŠ‰‡ˆ‹ŒŒ‹•rA '%$&.:BGD7" /3100-.:@,-@ISYWL@4-*)),/2788>JQO7%;^Ž·²¡¡¡ Ÿ¡¢¤¤¡¡¡¡¡¢¢¢¢¢££¢¡¡¢¡¡¢¢¢¢££¢¢¡¡¡ ¢¢¢¢¢££¢ Ÿ ¡¡ ¢£¡ŸŸ¡ ŸŸŸŸŸžžžžžœž Ÿžžœ›š››š™š››š™™š››™™˜—™›˜—˜˜˜——™™˜—˜˜—–•”•–••¨´´³´±¬©¦¡œ“’‘‘‘“”•–••“’’““””””“’’‘’““““‘‘““’’‘‘‘’’’’‘‘’‘‘’‘‘“•“’‘‘‘‘’“’ŽŽŽŽŽŽŒŽŽŒ‹‹ŒŒŠŠ‹‹Š‹Œ‹Š‹‹ŒŒ '19>?=:8:99=@@=71+('%"!"" %,>[u~„‡Š‰‡‡ˆ‡†„„„……{iM-#,‹ •’’‘›–oL( !&7Vs†ˆ ¡š——šžš™š™™š››œœ››™˜’œš–šž ¡ ¡¡ŸŸŸ Ÿ ¡¡ ¢£¢¡¡¤¥¤£¤¥¤¥¥¥¤£¤¤£££¥¥¥££¦¦¤£¤¤¤¢¢££££££¢£¥¥¤££££¢¡ŸŸ›š˜‘ŽŸŒ9_jeJ&.Wq¤”ŽŽŽŒŒ‹Š‰ŠŠŠ‹‹ŒŽc3 '&&(0>FJIC0!)-/0-/@F1 4AKPNE<3-*+++-169;ALQL2%:_¸´£¢¡ ¢¤¥¥¢ ¡¡¢£¢¢¢¢£¤¢¢££¢¢¢££££¢¢¢¡¡¡ ¡£££¢¢£¢¡ ¢¢¡¡£¤¢žŸ¡¢ ŸŸŸ ŸžžžžžŸžžžž›™š››™˜™›œ›™™š››š™˜—™š™——˜—–—™™™˜™™—•”“”••–——˜—˜™™—••–•”“’““””••””“’’“’’’“’‘‘‘’““’’‘‘‘‘’’’‘’’‘Ž‘’“‘‘‘ŽŽŽŽŽŽŒŒŽŽŒŒŒŒ‹ŠŠ‰‰ˆ‰ŠŠŠ‹‹‹(07:;96666:;:60+'$"!!!! !%-Bbx€„‡ŠŠ‰ˆ‰ˆ†………††‚}fH+:’¡’“’•xgE'!!"-C`r„‘Ÿš™—–šŸš˜™˜™›œœœœœ›˜œ¡Ÿžžžž ¡¡ ŸŸŸžžŸ ¡¡¡¢£¢¡¢¤¤£££¡ ¡¤¦¦¤££¢££¤¤¡¢£¦¥¡¢¤¤¤£¢¢¢¡¡££¡¡£¤£¡¢¤¤¢Ÿšœ››˜“¤ŽRqyQ#:`}¢“ŽŒŒ‹Š‹ŠŠŠŠŠ‰”—U''''*2?HLMJ;!(,-*/AG3!"7DHE@:4.))+++-39<BMPH.#7_“¸³¢¢¡¡ ¢£¤¤¢¡¢£¤¤¤££¤¥¥££¤¤£¢¢££££¢¢¡¡ Ÿ ¢£¢¡ŸŸ ¡¡¡¢¡¡¢¤¢žž ¢¢ ŸŸ ŸŸžžžžžžžœœœ›š››š™™™š™™™™™™šš™—˜™™™————–——˜˜˜™™—•“””•••–—–––——–•••••”””””““”“’’‘‘’’’‘‘‘‘‘‘‘‘‘’’’ŽŽ‘‘‘‘ŽŽŽŽŽŽŽŒŒŒŒŒŒ‹‹ŒŒ‹ŠŠˆ‡‰‹‹Š‰ˆˆ'.47620004542.*&#!!!"! !! $.FczŠ‡‡‰Š‰ˆˆ‡…„„…††ƒ‚|aC( F•™ŠŒ‹‡ŽŒt_; "!#5Mhn…–œ˜™™˜žŸŸ›šš™š›œŸœž ¡¤¢¡¡ ŸžŸŸ ŸžŸŸŸŸ ¡¡¡ ¡¢¢¢¢¢¡¡¢¢¡ ¡¤¦¦¤££¤¥¤¥¤¢¢£¤£ŸŸ¢£¤¤£¢¢¢¢£¤¢¢¤¤¤££¤¤¢œ›šœ˜•””¤“%VU$"@c ŽŽŒ‹Š‹‹ŠŠŠ‹‹‰–ŸB'''*4CKOQPD,"+-*/BG5' /?A>;862*'')*.5;;AMOC)"4Z”·²£££¢¡¡¢¤¤££¤¦¦¦¥¥¥¥¥¥££¥¤¢¢¢¢¢££¢¡ ŸŸ ¡¡ ŸžŸ¡¢¢¡¡ ¡¡¢¡ ¡¡¡¡ Ÿ ŸŸŸŸžžžžœ›œœœœ›š™šš™™šš™—˜˜˜˜˜™˜——™š™™˜—————––˜˜˜˜˜–””•––••–•••——–•””••”“”“’’“”“‘‘’‘‘’“’‘‘’’’’‘‘’’Ž‘‘‘‘’‘‘‘‘ŽŽŽŽŒ‹ŒŽŽŽŽŽŽŒŠŠŠ‹‹‹‹‹Œ‹ŠŒŽŽŒŒ‹‰‰Š‰‡ˆŠ‹‰‰ˆ‡%*/0-++,0222-($! ""! !"" ".Fc‘‹ˆˆŠ‰ˆˆ‡…„„……„ƒ…‚aE'#L•–‡Š‹ˆ‰…hL*!%#%<Uln|–˜šœžŸžœ›››š™š›žœšŸ¡¢¢¡¡¡¡¡Ÿžžœœž ££££¢¡ Ÿ ¡¢¡ ŸŸŸ ¢¢¡¢£¤£¢¢¢¥¥¤¤¥£¢¡¡ ŸžŸ ¡¡¡¢£¤¥¦¦¤£¤¤¥¥¤£¢ œžŸž›™œ˜••˜£9L’“W&#@a|˜ŽŽŒ‹‹‹‹‰ˆŠŒŒŠ—.'&&)5DLNQRL=%(/*.AG7,+!*:<977:7.&$'*.588@MM=%!4R±«¢£¢¢¡¡¢££¤¥¦¦¦¦¥¥¥¦¥¤¢£¤¢ ¡¡¡¢¢¢ ŸŸ ¡ŸŸ¡¡ ŸŸ ŸŸ ¡¢¢¡¡¡¡¡ ¡ ¡¡ ŸŸŸŸŸŸŸŸžžžŸœœœ›ššš™™™™š››™˜˜˜™™™™˜——™››š˜—˜˜——–•––––—––•–—–••••”“”••””““““““‘‘“•–•‘Ž“•”Ž‘’“”•”’‘’“““‘’’“‘ŽŽ‘’‘‘’’‘‘‘’‘‘‘ŽŽ‹ŒŽŽŽŽŽŽŽŒŠ‰ŠŒ‹‹‹Œ‹‹‹ŒŒ‹Š‰‰‹Š‰‰ŠŠ‰‰ŠŠ$),*((+.144/(# !! !!! "-D`~’‰‰ŠŠŠˆˆ‡…„„„ƒ‚…†dE(&M˜¡ŽŒˆ{Y=%%($ )?aƒ†Œ•–™œžŸžš››œ›™˜™š›œž¡£££¡ ¡¡¢ŸœžŸœœž¡££¤¤££¢¡ ¢¡ŸŸŸŸ ¡¢¡¡¡¢¡¡¡¡¢¢ ¢¤¥¤¢¡ ŸŸŸŸŸ ¡¢¤¦¦¦¥¤¥¤¤¤¤¢¡ ¢£¤ š™™–”••ž‹U]™ V'$Af€œ‘‘ŽŒŒŒŒŠˆ‰‹‹Ÿœ'&%*6EKNSVVL3%0,.BH924)-6658<;4)%'*-367@NL8"5X–·¬¢££££££££¤¦§¦¥¦¥¥¥¥¥¤£¤¤¡ ¡¡¢¢¢¢¡ŸŸ¡¡ Ÿ¡¡ ŸŸŸ ŸŸ¡¢¢¡¡¡¢¡ ŸŸ ŸŸŸŸŸŸžŸžŸŸžžžœœœ›™™šš››››œ›šš™˜˜šš™™˜—˜š›š—–˜™˜—•””•••––––––•”••”“““””•”“’’””“‘’•––•’“”“’‘’’’“””””“‘’““’‘““‘ŽŽ‘‘’‘‘’’‘‘ŽŽ‘‘ŽŽŽŽŒ‹ŽŽŽŽŽŽŽŽŒ‹Œ‹ŠŠŠ‰ŠŒ‹‹‹‹‹‰ŠŠ‰‰ˆˆ‰‰‰Š‹ &))(')-0561)# ! #-D]yŽŒ‰ŠŠŠˆ‡ˆ‡†……„€bD'+Zš£”‘’’‚vT1%'$!!0Hd‹’ž™™›œš—˜š››š˜—˜™›žŸ ¢¢¡ŸŸŸŸ›šœŸ ŸžŸ ¢¢¡¡¢££¤£¡ ¡¢¡ŸŸŸ ¡ŸŸŸŸ Ÿž˜›£¦¦¤£¢¡¡¢¢¢¤¤¢¡£¥¦¦¤¤¤£¢¢£¢¢¢£¤¥žš™™–“””˜ll¬N"%Chž•’’’““‘ŽŒ‹ŽŒŠŠ‹‹Š¢š&&'+7CINTYZUA*(.CI;66)0547;=:0&$)-156?NJ21`ŸÀ¼¡ ¡¢¢££¤££¦¦¥¦¦¦¥¤¤¤¥¤¤£¢¡¢££££££¢¡¢£¡ ¡¡ ŸŸžŸ¡¡ ¡ ŸŸ ŸŸŸŸžžžŸŸŸžœœœœ›››œœœœ›šš™—˜šššš˜—˜™š™——˜™˜–••••–––•–––••••—–”““””••“’“•–•““””””’‘’’““”•”•”“““’‘’“’‘’“’‘’’‘‘‘‘ŽŽŽŽŒŽŽŽŽŽŽŽŽŒ‰‰‰ˆ‰Œ‹ŠŠ‹‹‹‹‹‹ŒŠˆ‰ŠŠ‰ˆ#&()()+.361*$!!!%/Fdx†‡‡‰Š‰†…‡ˆ‡†…„‚|u^B$3a”™“˜‘~pJ+ %'$#&9Sc ›––š››™™™›œ™™ššš›žžŸŸ ¡ žžžžœ™™œŸ ¡££ Ÿ ¢¥¤£¢¡¢£¢ ¡¢¤¢¡¡¡¡¡¡¢¡ ŸŸž¤§¦¤¤¤¢¢££¥¦¥£¡¢¥§¦¤¤¤¢¡¢¤¤¤£££¢Ÿ›™˜—••”™‘mlŸ±P!'Dc¦˜““”“”“ŒŒŽŠ‰‰Š‰¢—%')-7CIOU[\ZK(,HN?61)!)347:=@8'"(.355;JG-.jž·µž¡¢££¥¥¤¤¦§§§§¦¥¤£££¤£¡¡¡¢¤¤¤¥¥¤££¤¤£¢¢¡¡¡¡ ŸŸŸ ŸŸŸŸ ¡¡¡ Ÿ žžžžžžŸ žœœœœœœœ›ššššš˜——™™™™˜—˜™™™——˜˜—–––———–•”•”””””•––”’’””••““”––•””“““”“‘“”““““””•”’‘‘‘‘‘‘Ž‘““‘ŽŽŽŽŽŽŽŽŽŽŽ‘ŽŒŠŒŒŒŽŽŒŒŒŒŒŠ‰‰‰‡ˆŠ‹ŠŠŠŠŠ‹ŒŒ‹ˆ‡ˆˆ†…!#'*+++-034/)%" !"#'0Hiy„†‡‰ˆ…ƒ†ˆ‡†…ƒ‚€{o\A":e–”–ub="#&&$&-A`m{Œš—•–™š›››œ›˜™œœœœœ››ž Ÿœ›œœœ›œŸŸ ¡¡¡¡¡ŸŸ ¢¡ ¡¢££¡ ¡£¤¤¤£¢¢¢£££¡ ¡¤¤¤¤¤££¢¡¡¡¢£¤¤¢¡¢¤¥¥¤¥¤¢¡¢£¤¤£¡¡ Ÿš˜——–•”˜Šnc¡µZ!)H`‚«™’’’‘‘‘ŒŠ‰‰‰ˆ¡‘%()-5AHNTZ\ZP2'JRA3++1%-358>D=(!&,2549HG)pœ¦¡Ž“¨¦£¤¥¥¥¥¥¥¦§§¥¥¥¤¤££¢¡¡¡¡¢£¤¦¦¤¤¥¥¥¤£¢¡¡¡¡¡ Ÿ ŸŸŸŸŸ ¡¡¡ Ÿžžž ŸžžŸ œœœœ›™˜™ššš™˜˜™˜™™˜—˜™™™˜—˜———–––——–•”””””•••–•“’“””••”“““”““““““”“‘‘”“‘‘‘‘’“””’‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŽŒŽŒŒŒŒŒ‹‰ˆŠ‹‹‰‰Š‹Š‹‹‹‹‹ŒŒŠˆ‡‡†……$# $'*-//02464/)&# !! !#$(/Dhv„……‡‡„ƒ…††…ƒ‚ƒ|nZA!?k’”†jT/!'(%"(2@gy€›™—™™šš›œœœ›š›œ››œœœ›šœ›šš››œžŸŸŸ ¡¡ŸžŸ ŸžŸ ŸŸ ¢£¢¡ ¡¤£¤¥¤¢£¤¥¥£¡¡¢¤¤£¢£¤£¢¢¢¡¢£££¡ ¢¤¥¥¥¥¦¤¢¢¢£¤£Ÿ Ÿš˜——˜——‘ng¢¶X *Ba~¤š“’’Ž‹ŠŠ‰‡£‰&(),4AJPW]_\Q7!GSA2(*91$/36=F@+"$)/34:LI'*n˜œ™Œ§¦¦¦¦¦¦¦¦¦§§¦¦¦¦¤£££££¡ ¡¢¤¦¦¥¥¥¦¥££¢¡¡¢¢¢¢¡ ¡ Ÿžž ¡¡ žŸ¡ŸžŸ Ÿž›œœžŸžžžž›š™šššš™˜™™˜———–—˜˜˜——————––••–••””””•••”“’“’‘‘’•–”““““’’““““““’’’‘’”“’‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒ‹‹ŒŒŠˆˆŠŒŒ‰Š‹Š‰ŠŠŠŠŠ‹Œ‹Š‰ˆ‡†„„… %%! #'-0489888960*'$ !"! !"&.Aey‚ƒƒ…‡…ƒƒƒ„„‚‚ƒ|n[A!Ek‘‹‹wk[C)$('$!(8Ho~†•œ›šš™š››šš›œ›™™šœ›˜—˜››š™šœŸŸŸŸ ¡ žžŸžžŸ ¢££¢¡ ¡¢£££¢¢£¤¥¤£¡¡¢¢¢ž ¢¢¢£¤¤¤¤££¢¡¡£¥¥££¤¤¤¤£¢¢£¢ ŸŸ›™—–—˜ ’|mi °I(Ad|›™’‘‘ŽŽŽŽŽŒ‹‹Œ‹‡œ‚!'(*,3@JQYac`R7&?L>0'*;5+34:C?.%%'-23<OJ$*k’˜š“›ª§¨¨§§§§¦¦§¦¦§§¥¥¥¥¦¨§£¡ ¡¢¤¥¥¤¥¥¤£¢¡ ¡¢¢££¢¡ ¢¡ ŸŸžž ¡ ŸžžŸŸœžŸžœŸŸžœœ››žžžž›šš›››š™˜™š™˜—•••—˜————————–••–––”“”••–•“‘‘““‘‘”–•””””““““’’’’““’‘‘’““‘‘‘‘‘ŽŽŽŽŽŽŽŒŒŒŒŒŒŽŽŒ‹ŒŠŠŠŒŒ‰Š‹‰‰Š‹Š‰‰‰Š‹Š‰‰‰‰‡†‡"&%! #)08=??=;:960*&# !"##! !$+>^x€€„‡…ƒ€€‚ƒ‚ƒ‚}q\A Mn“ˆŒocQ:" &)&" *=Ns€Š˜š›œœš˜˜š›™˜˜›™™˜šœœ›˜––šž›š›žžžŸŸœžŸŸžŸ ¡¢¢¡¢¢ ¡¢£¢¢¢¢£¤¥¤£¢¢¢¢ žŸ¡¡£¤¥¥¥¥¤££££¤¤£Ÿž¡£££¢¡¡¡ ŸŸŸš—–••š¦uueª®8'@g…Ÿ“‘ŽŽŽŒŒŒ‹‹‹…—{!'(+.4@KR[egdW;-*2@8/%);7 '48=D?0*((+/09LG +h”¡ ‘€˜§©©©©©©¨¨¨¨§¨¨§¥¥¦§¨§¦¤££££¤¤¤£££££¢¡ ¡¡¡£££¡¢£¡ Ÿž ¡¡ ŸŸ ŸžžŸžžœ›œœœ›šœœœššš››š™™˜™š™˜—–•”–————––––—–•••••”“••••”’‘““’‘’“•””•••”””““’‘’“’‘‘‘‘’’’‘‘’’’ŽŽŽŽŽŽŽŽŽŽŽŒ‹ŒŒŒ‹‹ŒŒŒ‹‹‹ŽŒŒŒŽŒ‰‰‹ŽŒ‰ŠŠˆˆŠ‹Šˆ‡‡‰‹Šˆˆ‰‰ˆ‡‡ %)*% &0>FHFA>;973.(# "###"! !$*;Wt€€€…ˆ‡ƒƒ„„‚zr\@Wy–‰‰‹tgQ8#'(%"!-@Z{†Š’˜™›œ›™——˜˜˜—•••–˜˜šœš”•˜¢œœŸž›Ÿ›œžŸ ¡¡¢¡¡¢¢ ¡¡ ¡¡ Ÿ ¢££££¢¢¢¢¡ Ÿ ¡£¤£££¢£¤¥¦¥¤¢¡Ÿ ¡¡¡¡ ¡¡ Ÿ›•••”•žŠttjº·8$<gˆ¡’ŽŒ‹‹Œ‹‹Œ‹Œ”y#$(,3>IOZeigZ?27.%340%'88*!08=B=2/,))*+5ID'[Œ£¤Œ~™§ªªªª©§¨©©¨©©§¦¥¦¦¦¥¤¤¤¤¥¥¤££¤£¢¢££¡¡¡¡¡¢£¢¢£¢ ¡¡ŸžžŸŸŸ ŸŸžžŸ ŸžŸžœœœœ›š›œœœš›œœœ›šš™š››™™™™™™————–”•–———––••–•••””””•––•””“’“””””““““’“””“““““’‘‘’‘‘‘‘’’‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽ‘ŽŒŒŒ‹‹‰Š‹Œ‹‹‹ŒŒŒŒŒŒŽ‹Š‹ŽŒŠŠ‰ˆˆ‹‹‹Š‰‰‹‹Šˆˆ‰‰ˆ‡†(+030' !*;NUSLB<962/,'! "##"! #(;Yr‚‚‚ƒ†ˆ†ƒ€ƒ……‚~yqZ>Z}Ž‡‡‹vgO2&*($""/Jd‡‡Ž“••”˜˜—˜—–––••••—˜™š™—–•—œœŸ ¡Ÿ››žš›Ÿ¢¡¡¡¡¡¡¡žŸ Ÿœœ ¢£¢¢¢¢¡¢¢¢ ¡ ¢¢¡¡ Ÿ¡£¤¥¥£¢¡ ¡¡ ŸŸŸ ¡¡¡ ŸŸ••••”švtl»¼3#7a‡œŒ‹Œ‹ŠŠŠŠ‹Š’‰f!!"$*9EMWafg]A4<9"$/2'&387+&28;943.('&%0GB&J‡¬¢†x™¥¯«ª«ªª©©ªªª©©¨§§§¥¥¥¤¤¤¤¤¥££¤¤£¢¢¢¢¢¡¢¢¡¡££££¢¡¡¡ ŸžŸŸŸžžŸžžŸžœœœ››››œ›››››š™™˜—™š™˜—————–——–””–———–”““••””““””•–•”””“““”“““““’’’‘’’‘‘’“’‘‘‘‘Ž‘ŽŽŽŽŽŽŒŽŽŽ‘ŽŽŽŒŠ‰‡‰‹‹‹‹‹ŠŠ‹ŒŒŒŒŒ‹‰‰Š‹‹Š‹‹Š‰ˆˆˆ‰Š‹Š‰‰‰‰ˆˆ‡ˆˆ†……36::4*!!.CX\XN?752.+)&!!"#"!! #)<[tƒƒ‚„ˆ‰‡†ƒ‚ƒ…†…‚~ytZ<]~Œ‡‰‰s^B)',)$"$4Oh|ƒ†’““”—˜———––—–•––—˜˜™™™š˜—˜˜šœŸŸžœ›ž›šœžŸžž ¡¢¡ žžžŸ Ÿ›¢££¡Ÿ¡ ¡¢¡¡ ¡¡¡ŸžŸ ¡¢¢¤¥¤¢¡ ¡¢ žŸŸ ¡¡¡ ŸŸŸ—–––• tpun¶µ"6bŠŽŽŠ‰ŒŒŠ‰ŠŠŠŒŽ|^!!0@IQX^c]C3:;)*6*&.6@7,23333/*(%".HC11|³œvyš ¬ªªªª«««ªª©¨¨¨¨¨¦¥¦¦¥¤¤£££¤¥¥££¢¡¡¢¢££¢¡££££¢¡¡¡¡¡Ÿœœžžžžžžœœœœ›š™œžœ›››™˜™˜——˜™˜––—˜˜˜˜—–”•–———•“’“•—–”““““”•”““”””“””““““’’‘‘‘‘’’’‘‘‘’‘ŽŒŽŽŽŽŽŽŽŽŽŽŽŒŒ‹ŒŒ‹‹Œ‹ŠŠŠ‹ŒŒŒ‹Š‰‰‰‹‹ŠŠŒ‹ŠŠ‰ˆˆ‰ŠŠŠ‰‰ˆ‡‡‡‡‡‡†…†7:?=5+"".DW\XL:220+'&%! !! #*>\s‚‚€‚†ˆ‡‡„‚ƒ„„~{yvY:`”ˆ‰‰jR5 !(*&"$*<Yo…‹’“””•˜˜–———™š˜–•–˜™ššššœš™˜š›œžžš—œœ›œž ¡¡ žžŸžžŸ ¡¡ Ÿ¥¤¢ žžžžŸ ¡¡¡ ¡¢¢¡ Ÿ ¢¢¢¢¤¥¤¢¡ ¡¢¡ŸŸŸŸ ¡¡Ÿž˜–––•–¦pwwu·°"8j‘ ‘‘‘‘ŽŽŽ‹ŠŒŽŠŠ‰ˆ‰ˆz`"#"%9IRV[daG5==+&7,(.8E>")10/020,'%"-ID/>oµ™knŽ˜°¯«««¬®¬«ª©©©¨©©§¦§§¦¥¤¤£¤¥§¦¥¤£¢¢££££¡¡£££¤£¢¡¢¢¢¡žžžžœœ›šš›žžœ›œ›™™™™˜˜˜˜—–––—˜—––•”•–––•”“’“•–•“’’’’’““““”•”’’’’’“““’’‘‘‘““‘‘’’ŽŽŽŽ‘‘ŽŒŽŽŒŒŽŽŽŽŽŽŽŽŒ‹‰Š‹‹‹ŠŠŠŠŠ‰ˆˆŠ‹‹Œ‹‰ˆŠŒŠˆˆˆ‡‡ˆˆˆ‡‡‡†……††………†‡16<:3)""+;KRPD610,)&$" !$,?[s‚€„…††„‚€|yywsS5c„š‰_H0%)(#$3Cbq„‘““”•—™˜–—˜™›œ™–”•˜š›››š›œœš™››››œœ›™™šœžžžžžŸ ŸŸžžŸŸž ¢££££¢¡ŸžžŸŸŸ ¡¢¢¡¡¢£¢¡££££¢ ¡¢¢¢¡Ÿžž ¡ žœ›’“”–••¥pov‚²±"9k‹œ‘’’’ŽŽŒŒŽŽ‹‰ˆ…„‹yU&'$,FSUY`\D5B@,!"2+*2;FA*'0.,/22+$!!-IE-:tºŽcj‚–¶´¬¬¬®«©ªªª©©¨§¦¦¦¦¦¦¥¥¦¦§¦¤£¢¢££££¢ ¡¡¢¤£¡ ¡¡ ¡ Ÿ žœžžœœœ››œœœœ››››š™™™™™™˜˜————˜—••––••–––•”””””••“’’’‘‘’“““”””’‘‘‘’““““’’‘‘“’‘’“”“ŽŽ‘ŽŽŒŒŒŒŒŽŽŽŽŽŽŽŽŽŽŽŽŽŒ‹Š‹‹‹‹ŠŠŠ‰‰Š‰‰Š‹‹ŒŒŠ‰‹ŒŠˆˆˆ‡‡ˆˆˆ‡‡‡…„…†ˆ‡††ˆŠ(-32,&"#)2<BA:20/,)'$! ! "*>[s€ƒ„„„…ƒ‚€}xxxvqO1h‰ž“[A(&*("':PjrŒž˜•””•˜š™–—˜™š››š–—˜š›œ›œœœœœ››œœ›š—š—˜ž žŸ ŸŸŸ žŸ¡£¤££¢¢¢ Ÿž ¡¡¡ ¡¡¢¢¢ ž¡¢¡¢¡ ¡£££¡ŸžŸ¡¡ žœ˜‘’“”•™¨sq|‡°±'Ak‡œ‘’’“’‘ŽŒŠ‡„„tE ()'!;PUY[R:3DC.'(((+2=GC1!))(-34++GB+=v¹t|~“¶¹®®®®®®¬«¬¬¬«ª¨§¨¨§¦¦§§§¦¦¦¦¥£¢¢£¤¤£¢ ¡¢¢ ŸžžŸ Ÿ ¡ŸžŸ ŸŸžœœžžœ›š™šœ›™™™˜˜™™™˜˜˜˜˜˜—••——••––••••••”””“’’’‘‘’“”””““’’“”””“’‘‘‘’’’““‘‘‘ŽŽŒŒ‹ŒŽŽŽŽŒŽŒŒŽŽŽŽŽŽŽŽŽŒ‹‹‹‹ŒŠ‰ŠŠŠ‹‹‹‹Š‰ŠŠ‰‡‰‹‰ˆ‡‡ˆˆ‡‡ˆ‡‡‡…„…‡‰‰‡‡‰‹!%'$"!#%*./.*)+.-+($! !" !(<Yr€‚……„„„…„ƒ‚~zzyzlH-mš’[: &)'# *?Uq‚Ž—˜—•””–˜˜–—™˜˜™šš™˜˜˜šš™›œœ›œ›ššœœœœ››—›Ÿ œžžžžŸ ¡¢¢ ŸžŸŸ ¡¡¡¡¡¢¢¢¡¡¡Ÿ›œŸ¡ ŸžŸ ¡¡¡Ÿž Ÿ¡¡ ¢¢¢¢ Ÿ ¡¢¡Ÿ›˜’’’“”™©lY|‰«®*Ek‰™‘‘’’“’ŽŒŠˆ†…n: '))")@NVXL1-?>/-3'#+07DD6("%,34**E>-@q´—x~x‡°¹®®®®®¯®¬¬¬ª§¨©©¨¨§¨¨©§¥¤¥¥¤££££¤£¢¢£¢¡¡¡¡ žœŸŸžžŸ ŸžžŸ Ÿžœ›œœššš›œ›™™™˜™™™™™ššš™™˜——™™—–––•”””•••””’’’“’’“”””“““““‘‘“““””“’‘‘’’‘’’’‘‘ŽŽŽŒŒŒŒŽŽŽŽŽŽŽŽŒŒŒŽŽŽŽŽŽŽŽŒ‹‹Œ‹ŠŠŒŒŒŒŒ‹‰‰‰ˆ‡ˆŠŠ‰‰ˆ‹Œ‹‹‹‰ˆˆ†…†‡ˆ‰ˆˆ‰‰!#$#! $*-+'# !"! ");Zr}ƒ……ƒ‚ƒ„„‚~zzxtbE*#oŽ—‡wW2#((%#".H\|Œ——–•””–––—˜™˜—™š˜——˜™™™››šššš››œžžž Ÿ Ÿžž ŸŸ ¡¢¡ŸžŸ ŸŸ ¡¡¡¡¢¡¡ œž ŸžžŸŸ¡¢£ Ÿ Ÿ ¡ŸŸ¡¢¢ ¡£ œš˜–’““•š©]M|Š¡ ,Jd…œ‘‘‘‘“’ŽŽŒŒ‹‹Š‰Œ’a6"%''$*DPSF,'9;259.+-2BE7-"#.0-(*C9/Gf¥’yx‚¯º®°¯¯¯°°¯¬««ª¨¦¨ª©©¨¨©ª©§¤£¤¥¤££££¢¢¢¤¥¤¢¡¡¡ ¡žžŸŸžŸŸžžŸ žžžœœœššš››šš™˜˜˜˜™™šš™™™™˜˜˜™˜––––•”“•••”“’‘’““’’“”““““”“’’’’‘’“’’‘Ž‘‘‘‘‘ŽŽŽŽŽŽŽŒŽŽŽŽŒ‹ŒŒ‹‹ŒŽŽŒŒŒ‹‹‹‹‹‹ŒŒ‹ŠŠ‹ŒŒŒ‹Šˆˆˆ†‡‰‰‰ˆ‡Š‹ˆ‡‡†………‡ˆ‡†…„ !$))%! ! #*<Yr|ƒ……„‚ƒ…„}||{r^D*#p’“”zjN,$(&#"%5Rc‚‰’••••••••––———˜˜–––—˜˜™œžž›™™››œ››œœœžŸŸžžžž žžŸ ŸžŸŸŸŸžŸŸ ŸŸ ¡ ŸŸ¡ œœ›œ ¢£¢¡ ŸžŸ¡ Ÿ ¢¢Ÿ ŸžŸŸž›š™˜•••–œ¨ZgŒ¨¬#-K^ –‘‘‘ŽŽŒŒŒ‹Š‹ŠŽ˜U)#%''%8JMA(%7828<2#'+0AD83+",+),*"+A3-Mg’ˆ~€y‡²¹¯°°¯¯°¯®«ªª©¨¦§©©¨¨¨©ª¨¦¤£¤¥£¢¢¢¢¡¡¢£¤¢Ÿ ¢¡¡¡ žžžŸ ŸžŸŸ Ÿœœœžœ›œœ›š›››š™˜—˜—˜™™™™——˜˜˜˜˜˜—••–—–••––•”“’’’“”’‘’”“““’“““’’’‘‘‘‘‘ŽŽ‘‘‘‘ŽŽŽŒŽŒŽŽŽŽŽŒŒŒ‹Ž‹Š‹Œ‹ŠŒŽŽŒŽŽŒŒ‹‹‹ŒŽŽŒŒ‹ŠŠ‹ŒŒŒŒŠ‰‰‰‡‡ˆ‰‰ˆˆ‰‹Œ‹Š‡†‡ˆ†††‡ˆ‡………!! %'&# #+=Zp|„ƒ…†…ƒƒ…ƒ€~~~~v`D)%t‡Šr\>&%'%!"*>Wj‡“’‘’””•–——––––˜™™™˜˜——™™™š›œ›››œœœšš™˜—šœœœŸŸ žžŸžžŸžžŸŸ Ÿ ¡¡ Ÿ¡¡Ÿœœœœœœ¡¢¢¡ ŸŸŸ¢¢Ÿ¡ žžŸŸžŸš™™˜—–––£¥Si€¬¯.Faƒ¦˜‘ŒŽŒŒ‹ˆ‰ŠF$'(&$!'=F=&$463:=6. $0BB;9/&+.55,#+<,)Gf‰~}s„©¸²±±°¯°¯¬¬¬©§§©©©¨©ª©¨¦¦¦¥¥¤¢¢¢¢¡¡¡¢£¡Ÿ ¢¡¡¡ žžžž ŸžžŸŸŸŸŸœœœ››œœœœ››œœ›™˜˜˜——˜———––—˜˜˜˜˜–•”•–—––˜—–”’‘’’““’‘’”“““’‘’’’’’’‘Ž‘Ž‘‘‘ŽŽŽŒŒŒŽŽŽŽŽŽŽŽ‹‹ŒŒŠŠŒŽŽŽŽŽŽŽ‹ŠŒŽŽ‹‹‹‹ŠŠ‹‹ŒŒ‹Š‰‰ˆ‡†‡ˆˆˆˆˆˆˆˆ‡…„†ˆ‡††‡ˆ†…†‡#"$'(&""+>[nv‚ƒ„…„ƒ„…ƒ€€€}gE&)n~}mS0 %&$ $0EZqŒ“‘‘’”•–—˜˜——˜œœš˜—˜˜˜š›œ›˜•—™š›žœœ›š™—–”˜›œ›šœžŸžžœœžžœœŸ¡¡ žš›Ÿ¡¡ŸžŸ ž›œžžœœœŸŸŸŸŸžŸ¡¡™œžŸžžžžŸ žœšš˜—–••¡¢Jh}Ž®¯-Lc‡¨–ŽŒŒ‹Ž‹‡‡ˆ–š2$&%$" "';8$"1317<<9+*AB??4& +3;<2%)4$&>ZŒŠ}{n}ž¶¹²²±°°¯®¬ª©¨©¨¨¨¨¨§§¦§§¦¥¤¤¤¤¤£¢¢£¢¡ ¡ ¡ ŸŸŸŸŸŸžŸžžžŸžœœœœœ›››œœ››››œœœš™™˜˜——————————˜˜———•”“•–––––•“’‘‘‘‘‘’“““’‘‘‘‘‘’’‘ŽŽŽ‘“’ŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŒŽŒŒŽŽŽŽŽŽŽ‹ŒŒ‹‹‹‹‹ŠŠŠ‹Œ‹‹‹ŠŠ‰‡‡ˆ‰‰‰‡‡‰‰ˆ‡……‡ˆˆ††ˆ‰‡…††#&% %()&#!"##! !+@]o}„…†……††„‚€{yk@"-otwxeM)"&'%!&5J_vŒŒ“––––———˜™›œ˜––—˜œŸ£œ—•–———š›œœœœ˜•”˜››œžŸžžžŸžœ›œŸ¢ ŸŸž›› ¢¢ žžŸžŸŸŸžœžŸŸŸŸ ž›› ŸžŸŸŸŸžœ›š˜˜˜–•š¢CfzŒ® /K_‡¨“‹Š‹‹ŠŠŠŒŽŽŠ‡†‡—•%%$$""+&-1 -114:>?5 #998>6+'2:;3%%*$@QŒŠ{pp|ž±»±²²°¯®®®®¬«ª«ª©©¨¨§¦¦§§¦¦¦¤¤¤¥¥¤¤¢££¢ Ÿ ¡ŸŸŸ ŸŸžžžžžŸŸž›››œœœ›››š™š›šššš™™˜——˜—––—–––––••––•””•–––•”””“‘’’‘’’‘‘‘‘‘‘“’ŽŽŽŽ‘ŽŽŽŽŽŽŒŒŒŽŽŒŒŒŒŒŒŒŒŽŽŽŽŒŒŽ‹Š‹‹Šˆ‰Š‹ŠŠ‰Š‹Š‰Š‹‹‰‡‡ˆ‰‰ˆ†…‡‰‰‡†„…†‡‡††‡ˆ‡††… '*($"'**(%# "%''$! !+B^s…‚„†‡‡‡ˆˆ‡…‚~}}|{e9!|uur\D'$'&#!(9Me{ŽŽ‘–——””•––™™™›šš˜•““–œ¡“””–——–—™›œœ›š›œŸžœžžžžž Ÿžžœ› Ÿžž›œ¡¢¢ žžžžžŸŸŸŸŸŸžŸ ¡¢¡ Ÿ›œžžž žššš™——˜—• ž5cvŠ²-0J[…¨’Š‰‹Œ‹Š‹‹‹‹ŒŒ‰‡††—’''&&##/. +24339@=*258>8-#".573&"%#>X”™vmlzŸ¬¶³²±°¯¯®¬¬«««««ª©©¨§¥¥¨¨¦¥¥¥¤¥¥¥¥¤£££¢¡¡ ŸŸ žžŸŸžžœœžžžœ››œœœ››š™™š›š™™šš™˜—˜™—–——–––––••–––••”•–•““••“‘‘““’’’‘’‘‘’’ŽŽŽŽŽŽŽŽŽŽŽŒŒŽŽŒŒŒ‹‹‹ŽŒŒŽŽŽ‹‹ŽŒ‹‹‹‹Šˆˆ‰Š‹‹ŠŠ‰‰‰Š‹‹‰‡‡Š‹Šˆ††ˆ‰ˆ††…††††††‡‡‡‡‡†$),,)##),,)($""&*+*($! !!!*Bauƒ…‡ˆ‡ˆ‰ˆ‡†ƒ{zxp_7!#‚umP9"''$!!*<Ol‚””™™˜•”””“šš™™˜˜˜–”–™›š˜––šš–˜˜˜—™™™œžŸŸžžžžžžŸŸŸŸŸžžŸŸžžœœœ¡¤¡žžž ¢¡ ¡¡ ¡¢££¡ ŸžžŸŸžŸ¡ šššš—•–——”¦˜:e„Œª³-0J`~š‹ŠŒŒŒŒŒ‹Šˆ‡…„—”))('%#(33&!1763/4BC/-4;?80('/54'!"#=J“žyscp¨¶µ²°°°°¯¬¬¬««««ª©©©¨¦§©©§¦¥¥¥¥¤¤¤¤£££¢¡ ŸžžŸŸžžžžœœ›œ›œœ›ššš›š›œš™˜™š™——˜™————––•–—––––––•”•–•““••“‘‘‘’“““’‘Ž‘‘‘’‘ŽŽŽ‘ŽŽŽŽŽŒŽŽŒŒŽŽŒŒŒŠŠ‹ŽŽŽŽŒŽŒ‰ŠŒŽŽ‹ŠŠŠŠŠˆˆˆŠŠŠŠŠ‰ˆ‰ŠŠ‰ˆ‡ˆ‰Š‰‡†……†…ƒ„…………………†‡†‡‡†#',/0-)#!#&+/-)&%%&)./-*&#! !*Bbw‚„…††‡‡†…„‚{ytlW8 -‡…|mF*&)'#!!+@Usƒ”‘•———–”’’•–––––˜™˜——˜˜™š›™›šš™˜———™››™›žžžžœœžžžœœžžžžžœžŸœ› ¢¢¡Ÿ ¡ žŸ ¡¢¡ŸŸ Ÿ ŸŸžœš™š™•“”––”¡.h‘¥¬&.J`x‹ŒŒŽŒŒŒŒ‹‰‰‰‡„)''&&',45/ !3887.2FH1!#/=D>6/##076+#"#>@©|qVgš§¸¶³²±±±°®¬««¬«©¨¨©©¨§§§§§¦¦¦¥¤¤¤££££¢ žžžŸŸŸžœœž››šš››œœ›››š˜™š››œœ›™˜™š˜—˜™™˜—˜–––•–––•–––––•””““’“’’‘‘’’“““““’Ž’‘ŽŽŽ‘‘ŽŽ‹‹Œ‹‹ŒŠ‹ŒŒŒŒŽŒŒŽŒ‹ŒŒŽŽŒŒ‹‹ŒŒŠ‹ŒŽŽŒ‹Š‰‰Š‰‰Š‹ŠŠ‹‹Š‰‰ŠŠ‰ˆ‰ˆˆˆ‡‡†…………„…††……†††††…………!$'*-021.*&$%(*-/-(%&'(+/0.+'$! !!"+Abwƒƒ„„„……„‚~ysjU6%3‰…zj>%"()&"!"/F]y…’’“––—˜˜–’‘’”••–——˜˜˜˜˜˜™›œœ›œœ››™——˜™šš™šœœ›š™˜˜™œžŸž›š›œœœžŸ žžžœœ›œŸ¡¡ ŸžŸžž ¡¡Ÿž ¡¡ ŸŸ Ÿœššš™•’’•–•¥ŒLkŒª© ,Jc|‘‹ŽŽŒ‹‹Š‰‰‰Š‰‡“~(%%%&)-264'.48:/2IJ3'!#8?<;4)#298/'"">:‘ªwpkh—§»·³²±°°¯¯¯®¬ª«¬¬ª©©©©©¨¦¦¦¥¥¦¦¦¤¤¤¢¢££¡ŸŸ ¡ žœžžŸŸœšššš›œœ›ššš™˜˜™šš››š™˜™š˜——˜˜˜™˜–””””•””––••••”“““’’‘‘’’’“““””“’’’‘‘ŽŽŽŽ‘ŽŽŽŽ‹‹ŒŒ‹‹‹ŠŠ‹Œ‹Š‹‹‹ŠŠŒ‹‹ŒŒ‹ŠŠ‹‹‹Š‹ŒŒŠ‹‹‹‹ŒŒ‹‰ˆ‡ˆ‰‰‰‰‰‰‰‰‰ˆˆ‰‰ˆ‡ˆˆ‡††……„…†……†††…††††…„„„……"$%(*+,/2220,('),-,*('''(+..-*'$!!##!#+?`wƒƒ‚‚ƒ„„ƒ€‚zvmY9'Fˆ‚xc4#''$ #4Ne‚†“”——–•–˜˜‘’”–˜˜—––————˜š››˜˜™š›œš˜˜šš™™™›œœ›™—–—˜š›žžž›˜—™šš›œž ŸŸŸ›œ››žžœœœž ¡¡ž›œžŸŸŸœžŸœ›—™š™”‘“•™¦Œmwƒ°¬ *Icv•ŒŽŽŒŒŠ‰‰ˆˆˆ‰ˆ„ˆs '##$&(,158-#.7:.2JJ4-+.78=8.$.672+"">B–©onlk¤½»²±±±®®®®«ª«¬«ªª©¨¨©¨§¦¥¤¤¦§¦¥¤¤£¢¢¢¢¡ ¡¡¡ŸžœžžŸŸœššš››››š™™™™˜˜™™™™™˜˜˜˜™˜—˜——˜™—•”””““”–—–••••”“”“’’‘‘’’“”““””“‘‘’“‘ŽŽŽŽŽ‘ŽŒŽŽŽŽŒ‹‹‹‹ŒŒ‹‹ŠŠŠ‰‰‹ŒŒŒŽ‹‹ŠŠ‹ŒŒ‹‹ŒŽŒ‹‹Œ‹ŠŠŠ‹Œ‹ŠŠŠŠ‰ˆ‰‰‰Š‰ˆ‡‡‡†††……†‡‡‡‡‡‡‡‡‡‡‡†……†……&&'())),/121.*'())'%%%%$%(**))(&%%&&%',=Zs‚‡ƒ‚ƒ‚‚ƒ€{wnZ:(Gwq\+#''#'<Xl‚‰––———‘˜™–‘“•˜šš—•”–—––——››™—˜™™™™™™˜——˜™š›œ›š˜–›žžŸŸ———™ššš™œœœžœ›œœœœšš›žžŸ ¡¢Ÿœœ›šššš›žœ™——™˜”‘’”—œ…qyƒ®°8)H`pœ”ŽŒ‹‹Œ‹ŠŠ‰ˆˆˆ†…„n%#$%').48;3"&69-2JJ5//"+5=7/*%043.!!?E˜¢woq`ŸÀ¼²±²²±¯®®¬¬¬«ªªª¨¦¦§¨§¦¤£¤¦¦¦¥¤¤¤£££¢¢¡¢¡ žžžžžžžœžŸžœšš›››ššš™™šššš™™™™™˜˜˜˜˜—˜˜——˜˜–•”••”“•——•••––•””“““’’’’“”“‘“”“‘‘“”“‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒ‹‰‰ŠŠ‹‹‹‹‹‹‹ŒŒŒŒ‹‹‹ŒŒŠ‰‰ŠŒŒŒŒŒ‹‹‹ŒŒŽ‹ŠŠ‰ˆ‡ˆ‰‰Š‰‡†‡‡††††……††††‡‡†……†‡†…†‡…„'&%%&%$&*,./-*'&$#! !#$%')*)))*+,0=Vjy€~~€€~~€‚„}xmX7'EqokS&"&&" .Deuˆ‘•˜———–˜™˜’‘“–˜™˜•“•——––”’—™™˜˜—–—˜˜••–—˜™™™šš˜™ŸžžŸ ›™šš›š™š›ššœ››œœœœœœœ›™™œŸ ŸŸŸ ›œœ™˜™šš›››™˜—––•‘’“•—…zz…¥®P%(F[jŸ”‹ŠŠ‹‹‹ŒŠ‰ˆ†ƒƒ‚h##$&')/58;8)39,1KL6,+#!6@91-#.44/>EŽ—vssUu½´³²³²°°°¯®«ª««©¦¥§¨¨§¥¤¦§¦¥¤¤£¤£££¢¢¢¢¡ ŸŸžžžœœœœžžœ›››šššš››œ›šš™˜˜™™˜˜˜™™˜˜——–•––––••––•–—•”••––•””“““’‘‘‘’’‘’““’‘’““’‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŽŽŽŠŠŒ‹Š‹Œ‹ŠŠ‹ŒŒŒ‹ŒŒŠ‰‰ŠŒŒŒŽŒ‹‹ŒŒŽŒŠŠŠŠ‰ŠŠŠŠ‰‡††‡††‡‡†……†††ˆˆ†„…†††…†‡††&%#"! "%(*+*)% #(,..--/135?Sbnuxzxyz{{}ƒ‚yiT.&Ecg_H$ "# "7Rm{Ž“”–——–•‘‘”˜•‘’•–––””–˜˜——•‘’—šš™–••–˜™˜—˜™™˜˜˜š›™˜šžŸœš˜Ÿ¡¡žœ››œš˜šœ›šš›žœ››œžŸŸžžžžœšžœš™››šš›š˜˜˜–”““’““‘’‘‚zz…¢©W$BW]™’ŽŒ‹Š‰‹ŒŠ‰ˆ†„„}_###%'+/35;;-#".7*1OP6*(#4C;2/()24-<K‘wf\QtœÃ¼´³²±°°°°®««¬ª¨§§¨¨§¦¦§§¥¤¥¤¢¢£££¢££¢¡ žžœœœ›š›››œœ›š™™™™™˜——˜™™™˜—––—––—•••––—–•”••••”““““’‘‘‘‘‘’’’‘‘’’‘ŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŠ‹ŒŒŽŒŠ‹ŒŒ‹Š‹ŒŒŠŠ‹‹ŒŒ‹ŠŠ‹ŒŒŠ‰‰‹‹‹ŒŒŒŽ‹Š‰‰‹‹Š‰ŠŠŠ‰‰‰Š‰‡†…………†‡‡…ƒ„……‡ˆˆ…ƒƒ………„…†††!! "&(**& &,035668889@N[dknpqrsuwz~‚…„Š„dL.&E``Q8! %>Zp{‹‘“’’”•”“”•““™–““““”–˜˜—–“”™›š˜••–—˜˜™™™™˜˜—˜šš˜––™šš™˜—œ››œš˜˜››š™›Ÿžœœœœœœœœœœœ›œœœšœžœšššš˜˜˜–““””•“’}y{‡¨i# <Tb˜’ŽŽŽ‹ˆˆŠ‹‰ˆ‡†…ˆvW$###&+/13:;-%*&%0'0PR8+'#"5E>70*"!+1+:M”¡”‚0+j›Å¾µ´±°°¯¯¯¬««ª«¬©¨¨¨¨¨§§§§¦¥¤¥¤¢ ¡¢¢¢£¤£¢¡¡ œžžžžœœ›œœššœœœœœœ›š™™™™™™˜—––˜™˜˜———————–”•••––•”••””“““’‘‘‘‘‘‘‘ŽŽŒŒŽŽŒŽŽŽŽŽŽŽŽ‹‹ŒŒŠŠ‹ŒŒ‹ŠŠ‹Œ‹Š‹ŒŒ‹‹ŒŒŒŒ‹Š‰Š‹ŒŒŠŠŠ‹ŒŒŒŽŒŠ‰‰‹ŒŒŠ‰‰‹‹‹‰ˆ‰‰ˆ‡††……†‡‡‡…ƒ„†‡ˆˆˆ†ƒƒ…††……………#%)*'!%-47:=>=>>==AKU]behjmoquy|ƒŠ˜‘dG*&D[YC*"# )C`x}†Œ‘“‘“––•”“‘”˜˜•’’’’“–™˜—•—›œ™–••––——™š˜——˜™™š™––—™šš›˜–›œœ››š™™š››šœ ¡˜›žžœœžŸ›œœžœ›œœŸš™™™™˜—–“’“”–”’•ˆzxx‰¡¯w18Sh˜’ŽŽŽŒ‹ŠŠ‹Š‰‡††pL$#"#%*/249:-(/,#"0NQ</)') 0=:71+'!.*8E‹¤°°dw™Äµ´²±±±°¯®¬««¬¬¬ª©©¨¨¨¨¨¨¨¦¥¥¦¤¢ ¢¢¢¤¤£¢¢¡ ŸžžŸŸžœ›››œ›šš›œœ›š™™™™™™™™˜—–•—˜————–––—˜—–•••––•••”““““’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŒŒŽŒŽŒŽŽŽŒŒŒŽ‹‹‹ŒŠŠ‹‹‹‹‹‹Š‹Œ‹Š‹‹‹‹ŠŠŠŠ‹ŒŒ‹Š‰Š‹Œ‹Š‹ŒŒ‹‹Œ‹ŠŠŠ‰ŠŠ‰ˆˆˆ‰‰Š‰ˆˆˆ††‡‡‡‡ˆ‡††…„„†‡‡††…ƒ‚„…†…„„„ƒ!!! !%),*$"-6;=?AA@@@??AHPUY\_bfiltxzŠ–’Ÿ–fJ*&AVT: %&",Gb}„••”“”––”’‘Ž“–˜—”’’’“–˜˜˜—˜šš—•“’“•–—˜˜—•—šœœ›˜“˜›››œ ¡¡ŸŸžœšš™š››››š›žœ“• žœ››žœš›œœœœœ›šœžžžœ™˜˜˜™˜”’’‘’“•”–™vyz‹œ©x65Qa“—ŒŒŒŒŒŒŒ‹ŠŠŠˆ†††‘fA""#$&).3785+*31%,HL=2.13(%3452., ,)8R~™¶¿ªŸ‘œÃ¾´³³²²±°¯®¬¬¬«««ª©¨¨¨©¨¨¨§¥¥¥¤¢¡¡¡¡¡£££¡¡¡¡¡ŸžŸ žœ›››››››ššš›œœ›š˜˜˜˜™˜˜˜˜™˜——˜—˜˜–•””•——–•”•••”””•”•”’‘‘‘‘‘‘‘’’‘ŽŽŽŽŽŽŽ’‘ŽŒŽŒŽŒŒŽŒŒŒ‹ŒŒŒŒ‹‹‹‹ŒŒŒŒŒŠŠ‰‰‰‰‹ŒŒ‹‹‹‹‹‹‹ŒŒŒ‹‹‹ŠŠ‹‹ŠŠ‰‰‰‰‰‰‰‰‰ˆˆˆ‡‡ˆ‰ˆˆ‡‡††††††‡‡‡†…„ƒƒ„††…„„ƒ&&%#!$*.,&$2<@@AA@?@@@?AFKNPSUX]aht€‰™˜¢˜gM*$>SN0$%"!0Id{‚…Ž•––•”••’‘”—˜—–”“””•–————˜˜–”’’“–˜™™˜——™œžžœœœœœœ žŸŸ›šš›œ›››ššš™–—šœœœœ›˜™›œœ›š›œœžžžœš˜—˜˜•“’“”•––—œz{{~‘–§|<2JZ”‹‹ŒŒ‹ŒŒŠ‰ˆ‡†††’]7""!#&*.364/)+53&'BG>769:,'/40.,#*)7Tv…¦° ª¤§Åº´³³³²±°¯®®¬««ª©¨¨©©©©©¨§¥¥¥¤£¢¢¢¢¡¢¢¢¡ ¡¢¢ ŸŸŸžœš›œ››››››››œœ›š™˜™™™˜——™›™˜˜™™™˜–•““”•–•••––•””””••”’‘’’‘‘‘‘’‘ŽŽ’‘ŽŽŽ‹‹‹ŒŽŽŒŒŒ‹ŠŠ‹ŒŒŠ‰ˆ‡ˆŠ‹‹ŠŠŒŒ‹ŠŠ‹ŒŠ‹‹‹Œ‹‰‰‹Š‰‰ˆˆˆ‰ˆˆˆ‰‰ˆ‡ˆ‰‰‰‰‡……††……‡‡††††…„ƒ‚‚ƒ……„„„ƒ-+)&$!#*/-%"1;==>?>=>A@??BCDILMNSZ`r‰˜™—¤˜bH(#:NF(!%$!$/Yn~‚…’••”““”’“™—–•”””””•–—–•––—–”•–™šš™—–—™›œœœž£žœ›šš™˜—˜™™˜˜˜šœœ››œ›š™™˜™™š›œœ›œ›˜—™›œ›š™šœœœœœœš—–••”““•—–••–šu{|€ˆ•¤I/E\™‹ŒŽŒŒŠ‡…†††‡šW,!!!!#(,032.)-61$"#9A=;<=7),61-,' ((5Tpw£zŒ£Ã¼´´³³²±°¯¯®¬««ª©©¨©©¨§§§§¦¥¤¥¤£¢¢£¢ ŸŸ ¢¢ Ÿžžžœ›››››œœœ››œœ›šš™™™™™˜˜˜šš™—˜š›™—–•”““”••”•––”““““””“’‘‘’’‘‘’’‘‘ŽŽŽŽŽ‹Š‹‹‹ŒŽŒŽŽ‹‹ŒŒŒŠ‹Š‰Š‹ŒŒŒŒ‹‹Š‰ˆŠ‹‹ŠŠŒŒŠ‰‰ŠŠ‰ˆ‰Š‹Œ‹ŠŠ‹‹‹‹Š‰ˆ‰‰‰‰‰ˆ‡‡ˆ‰ŠŠ‰‡……††……††…………„„ƒ‚‚ƒ„„„„„„30-)'#!(+)!!/7;::;===?@>==<>DEEFNWco|š™˜£–_C$ 5F="#&#*<d|‡ˆ‰‘“”•”””“‘‘‘‹”“““”•––—˜˜–”•—™™™—–˜™š™—–—˜˜–•˜žœœšš™—•••–––•™›œžž››œ›šœ››œ™˜™›œœ›žžœ››œœš˜—•““”•——–•”•˜wz|‚‡‹Ž¡†T+=\ž—Ž‹Œ‰„„…†…žR!#%!"" !$).11-(-60%&-%-79>A?5)!'74//.*!&'3Um~¬£de«À¶µ´³³³±°¯®¬¬««ª©©©©ª©¨§¦¥¥¥¥¤£££¢¢£¢ ŸŸŸŸŸŸ Ÿžžžžœ››œ››œœœ›ššš›ššššššš™˜—˜šš˜–––•““”–•••–•“““’’’’’“’’’‘‘‘‘‘Ž‘ŽŽŽŽŽŽŽŒŽŽŒ‹Œ‹‹‹ŒŒŒŽ‹Š‹‹ŠŠŒ‹ŠŠŠ‹ŒŠŠŠ‹Š‹ŒŒ‹Š‹Œ‹Š‰ŠŠˆˆ‰‰‰ˆˆˆŠ‹‹ŠŠ‹ŠŠ‹‹‰ˆ‡ˆ‰‰ˆˆ‡‡‡ˆ‰‰‰ˆ†…„………„„ƒƒƒƒƒ„„ƒƒƒ‚‚‚ƒƒ‚‚52/,(""$#!,27768;<:9;:998:>??AJUan€––––‘Y?!-;4#$"!2Hi{‰‹’”•””““’‘’‘ŒŒ’“•–—˜™š™—••—™š™—•——˜˜—––––•’”—˜š›››š™˜–””•–•””™›ž››››œœ›™šœœš™™˜˜˜š›œœ››œœ››››š˜——•”“””””“’“”™so{ƒ‡¥Y'3P ™ŽŽŒŠ‰Š‰‡„…‡‡…‹žD$*"!"!!&,00,'-50')0*%3>A=3--,$ .00210(''1Rf…¼«xlrˆ¦³¼¸³³³²²±¯«ªªª©©©ªª©¨§§¦¥¥¥¥£¢£££££¢¡ Ÿžž Ÿžœ››œœ›™šš››œ›ššš™˜™š›š˜™™˜™™˜—–—–•””•–•””””’’’‘’’’’““’‘‘‘‘’ŽŽŽŽŒŒŽŽŽŒŒŒ‹‹‹‹‹ŒŒŒŠ‹ŒŠ‰‰Š‹ŠŠ‰‰‰ŠŠŠŠŠŒŒ‹ŠŠŠ‰‰ŠŠŠ‰ˆ‰‹‹‹Š‹Œ‹‰‰‰‰ˆ‡ˆŠ‰ˆ‡‡‡‡ˆˆ‰‰ˆ‡†…„„……„ƒ„„ƒƒ„„„„…ƒƒ‚‚‚‚‚2/.,% *05757::9:98777:<<<@HS`n€‘“‘›e@ $/+!$#!$9Rqy’š’”•”””“’’’’‹Š‘”–˜˜—˜™™™—–—˜™™™—–––——––•–—–•“–——˜™ššš™™˜––˜™•‘‘™›œœ›œœœ›™™œ›˜—–—˜˜˜š›œšš›œ›œœš—––••••”‘‘‘“”žšai{‚„ˆ¢‘[$.I¡œŽŒŠ‰ˆˆˆ‡ˆ‰‡…‹Ÿ6&-#$+//+(,1.'*1-!0CD=417:1#!*.133+('2NNy¼©Tu}€–¢®´¶´±²²±°¬««ª©©ªªª©§§§§¦¦©§¢¢¤¤¤¤¤¢ŸŸ Ÿž Ÿœœœœœœœ›™˜˜˜™›œ›™™™˜˜™š™˜—˜˜———–•–˜—•”•––•”””””’‘’““’’’’’’““’ŽŽŽŽŽŽŽŒŒŽŒ‹ŒŒŒŽŒ‹ŠŠŠ‹ŒŒŒŒŒŠŠŠ‰‰‰ŠŠŠŠŠ‰‰‰‰ˆ‰ŠŒŒ‹‰‰Š‰‰Š‹‹Š‰‹‹‹‹ŠŠ‹Šˆ†ˆˆ‡‡ˆ‰ˆˆ‰‰ˆˆ‡‡ˆ‰ˆ‡†…ƒƒ…††………„ƒƒƒƒ„„ƒƒƒ‚‚‚‚-*(% ).3666889<<977:==<>BJS`o}‡‰ŠŒ—“lE %$#%# &=Xx…™˜’‘’””“““‘‘“”“‘ŽŒ’”˜™—–———–•••••––•––••••––•—˜›œš™˜˜˜˜˜˜—˜˜˜˜šš™—˜œœ›šššš™—˜œœš˜˜™™˜—˜˜˜˜™››š›œš–––••””“’’“•–¤ŸQc~††‰Ÿ’d +G ‹‹‹‹‹Šˆˆˆ‡‡‡…ƒ”š#', )00,)*-+'*10* +AB=52:>5(#+0680 )&"!5R@pÁ³5fƒ‚‰ž¬¸²°°±°®¬¬ª©ªªªª©§¦¦¦¥§©§¤£¥¤£££¡Ÿ žž žœ››œœœ››œ™˜˜˜—˜›œš˜˜™™™™™—––—˜——–•”–˜˜–••••”“““”•”’’“’‘’’‘’””’‘’‘ŽŽŽŽŽŽŽŽŽŒŠ‹Œ‹ŒŒŒŠŠŠ‹Œ‹‹‹ŒŒŠ‹‹Š‰‰ŠŠ‰Š‹Š‰‰‰ˆ‰‰‹‹‹Š‰‰Š‰ˆŠŒ‹‰‰ŒŒŠŠ‹‹Šˆ‡ˆˆ‡ˆŠ‰‡‡‹ŒŠˆ‡‡‡‡‡‡‡†……†‡‡†……„ƒƒ„„„ƒ‚ƒƒ„…††'&#!',1567889;<;:;>@@?@DLUbnv}‚†ˆ‹fA $#")B^}Ž›”““‘‘”””“’’”–™–”’’•š™––˜™—–•“““”–”’”–•••–˜—•–˜œœ›š™™˜———˜˜˜šœ››œŸžœ›˜˜™™™™˜™œœœœœœš˜˜™˜˜š››ššœ™––—•••“’““”””ž£I`|††‰Œ™’j$G›•Š‹ŠŠŠŠ‰‰ˆ†„„ƒ€&((10,))*(&)/0.(#7>;20:?9.(1:>7'!,& $$"4RAqÌÃ'Rƒ‹‰Œš¥²´¯°°¯®¬ªª«ª©©©§¥¦¦¥§¨§¥¥¥¤¢ ¡ Ÿ ŸŸ ž››œœœœ›››š™™š™˜šœœ›™™š›šš™—––—˜—–––•–˜˜—–••••”“’”–•“““’‘’’‘‘’“’‘’‘ŽŽŽŽŽŽŽŽŽŽŽŽ‹Š‹‹Œ‹Š‰Š‹ŠŠŠŠ‹‹‹ŠŠŠ‹ŠŠŒ‹Š‰Š‹Š‰‰ŠŠ‰‰ŠŠŠŠŠŠŠ‰ˆ‰Š‰ˆŠŒ‹ˆˆŒŒ‹Š‹‹‹‰ˆˆˆˆˆ‰Š‰‡‡‹ŒŠˆ‡††…†††††††††………„ƒ„……„ƒƒ„………##!%+15789:979;<=@BCCCFOV`ksw~„‚ƒƒ`: !.Hb|Š–““’Ž‘“”’‘“–—™––“‘’–˜™˜””˜™—––”’’”•“’––••–˜˜—–—š›››š™™–”•—˜˜—™™™™››š˜š›™—–——••——–™™›œžœ™—™š™™š››ššš›—–—–•••“‘’’“““”žUbz…‡‰‹–Žd,=——ŠŠ‰ŠŠ‰ˆˆ‰…‚‚‚„#$#,,*)**&%(/22-#&36+(5;91" -9?;,%/("#$0O9pÐÉ1!†’“›«±¯°°®¬¬¬«ª«ª©¨©©¨§¦¦¦§§¦¥¦¦¤¢ ŸžŸ Ÿžžž››œœœœ›ššš›œ›šš›œœ››››ššš™˜˜˜———˜™—––——–•”••”“’“•”“’’‘‘‘’‘‘’’‘‘‘’‘‘‘ŽŽŽŽŽŽŽŒŒŠ‰‰ŠŠŠ‰‰‰‰ŠŠ‹‹‹‹‹Š‰‰ŠŠŠŠŠŠ‹‹Š‰‰ŠŠ‰‰‹Š‰‰‰‰‰ˆˆŠ‹Šˆ‰Š‰‡ˆŠŠˆ‰Š‹Šˆ‡‡‰‰‰ŠŠŠˆˆŠŠˆ‡‡†…………†††…††…„……ƒ‚ƒ…†…„‚ƒ…………""!#+2579::979;<>ACDEGKRX^hoty~~~]6$3Lc{‡’“’Ž’”“’”––‘“•”‘’”–™”’–˜—–——”’“•“‘“––•””•—™™˜˜™™™˜––”’“˜——————˜š˜—–˜™˜—˜™˜””––•••—š››š™˜™š™š›ššš™˜––—˜—•••“‘‘‘’“’’šQfzƒ‡‰Œ˜Š^(0œ‰‰ˆˆˆˆ‡ˆŠˆ„‚}!$((()*'&(-142)'.$&6;7/):C@0)3, !"-K(eÏÍY/„ŒŒŒŒ“¥²±°®¬¬¬¬¬¬«©¨©©¨§§§¨§¦¥¤¤¤£¢¡ Ÿ ¡¡ ››œœ››œ›››œœš™šœ›ššš›œœ›šššš›š™™™˜˜˜™™—–••••••••”“’”–•’‘‘‘‘’‘‘‘‘‘‘‘‘‘‘Ž‘ŽŽŽŽŽŽŽŽŽŒŒŒ‹Œ‹‹Šˆˆ‰‰‰‰‰‰‰Š‹‹Š‹ŒŒŠ‰‰ŠŠˆ‰ŠŠ‹‹ŠŠ‰ŠŠˆˆ‰‰‰ˆ‰ˆˆˆ‰ŠŒ‹ˆ‡‡‡‡ˆ‰‰ˆ‰‰‰‰‡†‡‡‡‡ˆ‰‰ˆˆ‰ˆ†……„„ƒƒƒ„„ƒƒ„…ƒ‚……‚€‚ƒ„„„ƒ‚‚ƒƒƒ„!""#,358::9879<=?@BDDHMSX\dkpsvyz~[4"*<Vl~‡Š’“’‘’““’””’‘•”’‘’’’‘”•”’””“’’“”““••”“’”–˜˜———˜—–””’‘‘“•–•””—˜———–——˜™š˜••••“”–—•–—˜˜—–—˜™š™˜”–—–••——–”””“‘‘’‘‘–auz‰ŒŽ™h%#‰›‰‰ˆ‡†††‡‰ˆ…ƒŠu !$$&))(&',35*!#"9?:1%9B=0,5, !+GXËÎnNƒˆ‡‹ˆŽ¢µ±°®¬¬««««©¨¨¨§§§§¨§¦¥¤¢¡¢¢¡ ¡¡ ›œœœ›››šš›š™™š›œ›™™™™™šš™™™šš˜—˜˜˜˜˜˜—–•””””–––•”“““••’‘‘‘‘’’‘‘‘‘‘‘‘’‘ŽŽŽŽŽŽŒŽŒŒŒŒŒ‹Œ‹ŠŠŠ‰‰‰ŠŠ‹‹ŠŠŠŠ‹‹‹‰‰‹‹‰‰ŠŠŠ‹‹‹ŠŠŠˆ†‡ˆˆˆ‰ˆˆ‰Š‹‹‰‡††‡‡ˆ‰‰Š‰‰‰ˆ‡‡‡‡††ˆ‰‰ˆˆ‰ˆ‡††…„„ƒ„……„„…„‚ƒ…„‚€ƒ„„…„‚‚ƒƒƒ„$%%#!#,358:;:89:=>@ABDFHMTZ`flptxz{}T5 !$1?`v†ŠˆŒ‘‘‘‘’””“““‘’•“‘‘‘Ž‘’““’‘‘’’‘’””“”••”““•–––——–––•”“‘‘”–––••–—˜˜–––˜˜™š™——–””•–——–˜™—–——˜š™˜–•–˜˜™˜—–•••””’’‘‘’’—“gw||‡ŒŽ››u%9‹ ‹Šˆˆ‡†……††…ƒ‰l!$)+(%%*47)"' 7@>4#2=;208. (C%PÊÐ<„‰‹Œˆ‹¢¯º°°¯®«ª«««¨¨©©¨§¦§§§¦¦¤¢¢¤£¡ŸŸ Ÿžžœœœœœ›››š™™š›œ›™™˜˜˜™™™˜˜˜™—————––—–•–••••–––––”““”“’’’‘‘‘’’’’‘‘‘‘’“’ŽŽŽŽŽŽŒŒŒŒ‹ŠŠ‹ŒŒ‹‹Š‹ŒŒ‹ŠŠŠ‹‹‹Š‹Œ‹‰‰ŠŠ‰‰Š‹ŠŠ‰ˆ††††‡ˆˆ‰‰‰‰‰ˆ‡†††‡ˆ‰‰Š‰‰‰‡††††……‡ˆˆˆ‡ˆˆ‡†………ƒ‚ƒ……†‡†„ƒ„„ƒ‚‚ƒ„„ƒ‚‚‚‚‚+*)'$ $+047:<;::;<>@A@CGILSZbhmosƒzP5 " &8Ji€ˆ‡‡‹Ž‘••“““‘‘“’Ž‘”“’’““’’’’“•“”••“’“••””–•““–—–“‘’–˜——–••–˜—••–˜™™™˜—–””•–––—˜šš—–————••”•—™™™˜–”••”””“““’“‘–‰Yuz†‹¤„&<Œ¦Š‰ˆˆ‡…ƒƒ„†…ƒ~i!(,($$)8:($1*1<@?1(%#8<3091%@CÈÐ’_‚‰‰‰}œ©»³°°¯®¬««ªª©¨¨¨¨¨§¦§§¦¦¦¥¤¤¥¤¢žžŸžžŸ Ÿœœœœœœœ›œ››šššš›™——˜˜˜™™˜——˜˜————––————•”––•”••••”““’’’’’‘‘’“““’’‘““ŽŽŽŽŽŽŽŽŽŒŒŒŒŠŠ‹ŒŒŒŒŒŒ‹ŠŠ‹‹‹‹‹ŒŒ‹‰‰ŠŠ‰ˆ‰Š‰ˆ‡‡…†……‡ˆˆ‰‰ˆˆˆˆ‡†……†‡‰‰‰‰‰‰‡†‡‡‡………‡‡ˆ‡ˆ‡‡††††…ƒ„……‡‡†„„„„„„ƒ‚‚ƒƒƒƒ‚‚‚‚.-,)% %+-159;;;;;<=@ABDHKOU^floju…†‰yQ6 *?Uq†Šˆ‰‘””“““‘Ž“•“‘’””’’””‘’–•”““’’•—–”’‘’•˜˜–”““•˜™—–•••——––•—œ——–”‘“—™—–—™š›—––———”””•–˜˜˜–”””“’“““””“’‘‘„orxˆŠ‹Š§Œ&)…Ÿ‰‡‡‡†„ƒ„…†…‚{{e%+'%(0><)(:5*5>@61,#2;/-73"<BÆÌ—z}zyƒ“™±®¯¯«««ª©¨¨¨§¦§§§§¦¥¥¥¤¤¤¤¥¦ ŸŸŸŸž›š™˜™››˜—˜˜˜™™˜––˜˜——˜˜—••–——”“””“’“”““““’‘’““““’’“““’‘‘’’ŽŒŽŽŽŽŽŒ‹ŒŒ‹‰‹Œ‹‹‹ŒŒŒŒ‹ŠŠ‹‹Š‹‹Š‹‹ŠŠŠ‰ˆˆ‰‰‰‰‡…„ƒ„……‡‡‡ˆ‡††ˆˆ‡…„„…†ˆˆ‰‰‰‰ˆ‡‡‡†……†‡‡ˆ‡……††……†…„ƒƒ„…†…„ƒƒƒƒ„„ƒ‚‚‚ƒƒ‚‚€,++("&+.048::::;;=?BDFHMOT]hpt{‰‘xR6 !1H[tˆ†ˆŠ‘’’‘’“‘’“‘ŽŽ‹‘’’“”’““’“••’’••“‘‘’“•—–”’ŒŽ•—˜——–““–—–“””•–——•“”—•“””‘‘•˜—••——˜—••—˜–”””“’“”””“’‘Ž‘‘’“’‘†trw€†‰‹Š—#4|š‡‡‡‡††……„„ƒ~zz[ %%().=>,,><&!.<>431* ,5('45"9!OÇË™’˜—— «¬£¡¬®¬¬®«ªª«ª©¨¨¨§¥¦§§¦¥¥¤££££££¡ŸŸŸ Ÿžžœœžžžžžœš™˜˜š›™————˜˜–••—˜—˜š™—–••——”’“““’““’‘’’’’”••””“’’“’’‘‘‘ŽŽŽŽ‹‹ŽŽŒŒ‹ŒŒ‹Š‰ŠŠ‹‹ŠŠŠŠ‰ˆŠ‹Š‰‰ˆ‰Š‰ˆˆ‰ˆˆˆ‡…„„…†‡ˆˆ‡‡‡…†ˆˆ††††††††‡‡ˆˆ‰‰ˆ‡‡‡‡ˆˆˆ‡…„…†‡…„……„ƒƒ„……„„ƒƒƒƒ„„„ƒ‚ƒ„ƒƒƒƒ€)'&$&,/0379889;<=?AEHJMOT_ju~Š˜žš‘vQ4"# $9Q^u…‡ŠŒ‘“’‘’‘‘’’’‘‘Ž‹‘‘’“’“””••”’’’““’‘““”––”‘Ž’–—˜˜—”’“•”“”••––—–“’‘‘‘“”’’•–––––•–––•––••••“’’““’““’‘‘Ž’’’’†wuy†ŠŒ‹ª.u’‡‡‡‡†‡†„ƒ‚€~znU &(,=>-->>-(=?753-%"+##38(6#UÆÇ•”™ž¥§¸Ã¼±¬«©¨ªªª©««ªª©©¨¦¥¦¦¥¥¤£¡¢£¤¤£¡ŸŸ ŸŸ Ÿ›šœžžžžžœ›™˜—™™˜—˜™˜˜—–•––––˜š™–•–––•”“’“““““’’’“““”•”””“’’’’’‘ŽŽŒŒŒŒŒ‹Š‰‰‰ŠŠŠŠŠŠŠ‰‡‰‹Šˆ‡†‡ˆˆˆˆˆ‡‡‡‡†…†‡ˆˆˆˆ‡‡†††‡‡‡†††‡†……††††‡ˆˆ‡‡‡††††…„„…††ƒƒ„ƒ‚‚‚ƒ„„„„ƒƒƒ‚‚‚ƒ‚‚‚€€€~}~%$!&,/149:977:<>?AEHJMPU_iw‰“šŸ’xP2#$!&=Vcy…‡ŠŒŽŽ‘’‘’“‘Ž‹’”•”’‘‘‘‘‘‘’•••“‘‘““••’‘‘’““••””•––•“’‘‘”–•”•––—˜™—””–—•”•••••“’“““’“”’’’’Ž‘’“”’‡zxz€…ŠŒŠž›+lŠ†‡‡†††…ƒ€~{jK!#"$)<=+,<=3*9?863/*#39./dÅÄ“‡rx–› §¬±®ª©¨©©©ªª©©©©§¥¤¥¦¦¥¤£¢¡¢££¢ ŸŸŸžžŸž›šœžžœœœ›š™˜———–—™™˜—–––——••—˜—•••••””””””““’’’’““““’‘“””’‘’‘‘‘ŽŽŽŽŒŒŒŒŒ‹Š‹ŒŒŒ‹‹Š‰‰ˆ‰‰‰ˆ‰Š‹‰ˆ‰Š‰‡††††‡‰‰ˆ‡‡‡ˆ‡†‡ˆˆ‰‰ˆ‡‡‡††‡†††††††…†‡‡†…†‡††‡††………„…††††„„„ƒƒƒƒƒ„ƒƒƒƒƒ‚€€€€}~~~! &,/049;;:::;>?AEHKOQV_hzŠ“™ ¥“zP1##"+AYkŠ‹Ž‹‹‘ŽŽ‘ŒŽŽ“•–”’‘‘‘‘‘’”““““’‘““’‘’“”•–•”“””••”“’‘“–—––––˜™™˜–•—˜•••–—–”’’““““““‘‘ŽŒ‹’”“„wxz}…Š‹Š¤Ÿ&dŒ††‡‡†„‚€~~}‚d@"'% ';>,,9:82#-99940.'#491!3(sÁÄ¢Ž`bs„oƒ ±´¯£Ÿ¨©ª«ª©©¨§¦¥¦§¦¤£££¢¡¡¡ ŸŸ žœŸžœœœžžœ››œœ›š™™˜™˜—™›š™˜—–—˜˜–••——––––•””••”“““’’’’’‘’’‘””’‘‘ŽŽŽŽŽŽŽŒŒŒŠŠ‹Š‰ŠŠ‹ŠŠŠŠ‹ŠŠ‰ˆ‰‰ˆˆ‰ŠŠ‰‰‰‰ˆ‡††………‡ˆˆ‡‡‡‡‡…†ˆˆ‰‰ˆ‡‡‡‡†…………„…†……†‡‡†„„„„„„„……………†††††……„‚‚ƒƒ‚‚‚€~~~~}}}~~&,/147::;<;;>?AFJLOQU_h{Ž—™š¢ŽxO/!" ! $2EZn…‹ŒŒŒŒŽŒŒŒŽŽŽŽ“””““’‘‘’’‘‘‘‘““‘‘“””’‘‘’“•”“’’“•–••”“““””••“’’”•””“”–—•”””••“’”–—–’‘‘’’’‘’‘ŽŽ‹ŒŽ”|puy{„‰ŠŠ§ Z{Ž…„†‡†„‚€~~}\3"(+%#9>-,4597(!1;<401.#"4<4# (S…½Ä´¥i…Šƒƒšœ—š–®«ªª©©©©¨§¦¦¦¥¤££¤£¢¡ ŸŸ žžžœœ›œœœœœœšššš›š˜˜™™™˜—––—˜—–•–—–•–—•””””““““‘‘’’’‘‘‘‘‘““’ŽŽŽŽŽŽŒ‹ŠŠŠŠ‰ŠŠ‰ˆ‰Š‹‹‹Š‰ŠŠˆˆ‰Š‰ˆ‰ˆˆ‡‡†††‡‡ˆˆˆˆ‡‡ˆ‡„…ˆˆˆ‡‡‡ˆˆˆ‡†………„…†……†‡‡‡………„„„ƒ„…………††…††……„‚‚ƒ‚€€€€€€€€€€ƒ#"!$-013589;<<<?@AFLMOQU^gz‘œ–š¡|Q. ! (9L_q„‹‹ŒŽŒŠ‹ŒŒ‘‘’‘Ž“”“”“’‘‘‘‘’’‘‘”••”’’•—–”““”••””•”””••–—–’‘“““’‘••’‘’“’“‘‘”–—–’‘‘‘‘Ž‘‘ŽŽŽŒ…{utx}„ˆ‰‹¢œVuŒ„‚„…„ƒ‚€‚Y+#*/,"6>/.56<:*"':?5044.6@8&#,r’·¾·«ŠŠˆ†‰Œ‘˜–‘Š‹’œ²¬«ªªªª©©¨¨§¥£¤¤¤£¢¢¢¡ŸŸŸŸžžžŸŸœ››œœœœ›™™™šœœš˜—˜™˜—–––——–––—–””••““““‘‘‘’‘‘‘’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽ‹Š‹‹‹Š‰ˆ‡ˆ‰ŠŠ‹ŠŠ‹Š‰‰ŠŠˆ‡ˆˆˆ‡††††ˆ‰ˆ‡ˆˆ‡†‡‡††‡‡†††‡ˆˆˆˆˆ‡……††……†‡‡‡‡†………„ƒƒƒ„„ƒ„††………„ƒƒ€€~~€‚€~~€€‚‚€ƒ'&$#!!*/0//26:;;;>ADINOPTY^ew¡› Ÿ}N) *AVgr‰ŠŒŒŽŽ‘’‹ŠŒŒŽŽ’Ž’“”“‘ŽŽ‘‘‘‘’‘ŽŒŒŽ’’’“”•”’‘•—•“‘‘““““”•–•”•–˜–’’““‘‘••’‘““‘‘‘’”•••“’‘‘ŽŒ‹ŽŒŽŒxuux}ƒ†‡‰£¦SqŒ„‚„ƒ‚€‚X%'.23,!3=//7:>:-*+4?62463&5A;('6ƒ–®°¯©|‘”‰„‘”——–”‘’›ª¯«ªª©©¨§¨©¨¦¤¤¥¤¢¡¡¡ žžŸžžžŸ Ÿœœžžœš˜™ššš›™———˜˜——––———–•––”“””’’“’‘‘’’’’’“’‘‘‘‘‘‘ŽŽŽŽŽŽŽŒŒŒŒŒ‹Š‹ŒŒ‹‰‰ˆ‡‰ŠŠŠŠŠŠŠŠŠ‰‰ˆ‡ˆˆ‡‡‡†‡‡‰‰‡†‡ˆ‡†ˆ‰ˆ‡†……††‡ˆ‡†‡ˆ‡……††……†‡‡ˆˆ†„…††„ƒ„ƒƒƒ„†‡†……„ƒƒ€€‚€~‚ƒ€€€€ƒ„‚))(&#',,,,-1689:>CKRUSSW]`cvŠ¤£¦—{p># /Kbpu~ˆ‹ŒŠŠŽŒŽ’’Ž’””“’‘‘‘‘‘‘’’Œ‹Œ‘“”““””•”’’–˜•’Ž‘’““”–—•“’”––’‘““’‘’”•”’”•”’’“““””””“‘ŽŒŒŒŽŒŒŒŽ’‘Œ‚vtv{‚„…‡ª/Jk†„„††ƒ‚‚’J+2672)4<//8>A<0/1&+842320&1A;(9‚—«®°«tŒ§¡š˜”“™š“‡“¢¯ª©©¨§§¨§§¥¤¥¥¤¢¢¢¡ŸžŸ¡¡ ŸŸŸžžžœœœœ›™š›š™š™˜———˜™™˜˜˜——––——•””“’’””“’“”••”““’’’‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŠŠŒŒ‹Šˆ‡‰‹Š‰‹‹Š‰‹‹‰ˆˆˆˆˆ‡‡‡‡‡ˆˆˆ‡……†††‡ˆˆ†„ƒ…††‡†……††…„„………†‡ˆˆˆ‡…„„†‡„ƒƒƒ„……††………„ƒ‚‚€~~€€€€}}ƒ++,(#"%&'()*,.16=FQZ]YX]aciu}‘”œ‹YU4#6Thv{~…ŠŒŒŽŽŒŒ‹‰ˆŠŽŒŒŽ‘‘Ž”“’‘’’‘’’’“””‘ŒŒŽ‘‘‘’’’’“””“’’“”“’‘’“”“‘”•“‘‘’“’’’“••“”••”“’‘’““”““’‘ŒŒ‹‹‹Œ”’Ž‡wsv|€ƒ……†ŒžAAk“†……††ƒ‚…3,3773-#.3,/48?<10/( +/120-&0B=)'(…Ÿ®©º®Xf¢¯³Ÿ˜›”’•œ§ª¨¨¨¨§§§¦¥¤¤¦¦¤¢£¢¡ŸžŸ¡¡ ŸžžžžŸŸžœœœœœ›šš›š™šœš™˜˜—˜˜˜˜˜˜——————–—–””“’‘’“”““””•••“‘‘‘‘Ž‘‘‘ŽŽŽ‘ŽŽŒŽŒ‹‹‹Œ‹ŠŠ‹ŽŽŒ‰‰Š‹ŠŠŠ‰ˆ‰‹‹ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ†…†ˆ‡‡ˆ‡…„„†‡‡‡†…………„ƒ„„ƒ…†‡ˆˆ‡†…„…‡†…„ƒƒ…††……†……„‚‚ƒƒ‚€‚ƒ~~~€€}~€‚+-/,&!!!!"#%',9HU`dcbcefksxƒ‡”‰RE*(=Wiw€ƒ‡‰‹‹Œ‹ŠŠ‰ˆˆŠŒŽŒŒŒŒ‹Ž‘’‘‘‘‘’““””’‘“””’‘‘‘”•”’‘‘‘’”“’‘‘‘’““‘‘’“‘‘‘’“’“”••”“”•––“‘‘’’’’’’’‘Œ‹ŒŒŒ’’Žƒzuw|…ˆŠ‹—M 7b‘‰…„„„‚‚ƒ}‚ˆ')28<90$ '(-.1;;2.+('(020-&5F?) <“«°¦»°3lˆ––žµ¸©¬«¡ž§©©©§§§§¦¥¤¤¥¥£¢¢¢¡Ÿžž ŸžžŸŸžŸ ¡Ÿœœœœ›š™š›™˜™›š™˜˜˜˜™™˜—–•–——––––•“““’‘‘““““”””•”’Ž‘‘ŽŽŽŽŒŽŽ‹ŠŠ‹‹‹‹‹ŒŽŽŒ‹‹Š‰‰ˆˆ‡‰‹‹‰‰‰ˆˆ‡‡‡ˆˆˆ‡††‡†††‡‡‡‡†…†††††‡‡……„„„ƒ„„ƒ„†‡‡‡††…………„„„„ƒ…†…„†‡†…ƒ‚‚‚‚‚~~€‚‚~}||~~}}~~~‚+-0/+& %3HWeklkjjkpuy|~…}W=( !-C]nzƒ‡ˆˆ‰ŠŠ‰‰ˆ‰‰ˆˆŠŒŒ‹‹ŽŽ‘‘‹Š‘’‘‘’’’’‘‘“•“‘ŽŒŽ“–”‘Ž‘‘’‘‘‘‘’’‘‘‘’“’‘’“’““’’“”•••“‘‘‘‘’’‘ŽŽŽŽŽŽ{xvxz†Š‹Œ’b/(Fƒ†ƒ‚€€ƒƒ{~")4;?>4$! &(-9:2,%'0'",..,&<KC*18H¡´®¥º±4^bnxŠ§¹¼¹§£¡Ÿ¥±®«ª¨§¨§¦¥¤¤¤¤¢¡¡¡¡ ŸŸœžŸžžŸœ›šš›š˜—™™˜™˜˜˜˜™˜—–••–——––––”“””“’’“““’“““““‘‘ŽŽŽŽŽŽŽŽŽŽŽŒ‹Š‹ŒŒŒŒ‹ŒŽŽŒ‹Š‰ˆˆˆˆ‡ˆ‰‰‰‰ˆˆˆˆ‡†ˆˆˆ‡††‡‡‡‡‡‡‡†……†……„…†‡†„„„ƒƒ„…ƒ„†††…†‡………ƒƒƒ„„„…„ƒ„‡†…„‚‚}~€€~~||}~~}}}}€(*.0.+$"1J^rvvwrrtvxyxwyqN:$#2Kfv…ˆ‰Š‹ŒŒŒ‹‰ˆˆ‰ŠŠ‹Œ‹‹ŒŽŽŽŽŽ‹‹Ž‘‘Ž’“’‘‘“’Ž’•”‘‘‘ŽŽ‘’’‘‘’’“’‘’“’‘‘“”•““’‘‘ŽŽ‘‘ŽŽŒxwwxy{‰ˆˆˆŠ“o0:ƒˆ„ƒ€‚~z~{ ,8AED8(!%#!%.;;1+#'51$)+*%"=LC*,KPV«º¨¦¾³6TfgcVJ^‡¢©©¢¢¡¢¦²¯«¨¨¨§¥¥¥¥£¢¡¡¡¡¡ ¡Ÿžžžžœžžœ›››™˜˜˜˜˜™™˜˜˜™˜——––—˜˜————–••”””””•“’’’’’‘‘‘’“’’‘‘‘‘‘ŒŽŽŒŒŒŒ‹ŒŒŒŒŒŒŒŒŽŒŠ‰‰ˆˆ‡‡ˆˆ‡‡ˆˆˆˆˆˆˆ‡‡ˆˆˆ‡‡‡‡‡‡‡‡††…„ƒ„„ƒ„…††…„„„„ƒƒ„„……………††„ƒ„ƒ‚ƒ„……„‚‚„…„ƒ‚‚‚~~€~~~}||||||||}~€'),./.)#!0J_u|{|zz{{{ywusmP5!$6Pn|€…ˆ‹ŒŒŽŽ‹ˆˆ‰Š‹‰Š‹Œ‹ŒŒŒ‹ŠŠŠŒŽŽŒŒ‘’‘‘ŽŽ‘“•”‘‘“’ŽŒ‹‹ŒŽ‘’‘‘‘’’’‘‘’Ž‘’Ž’’‘’‘ŽŽ‹‹ŠŠ‹ŒŒŽ‹ruvwx|„†…„ƒŽ{A#9’Š…„}||}|z„s/>FJF:*!"#(9:0)#&01+#((%"<JC+.M[_¯¼¦¥ÁµC\gffjiNQp‚š¶²¢ ¡¦©§¦§§¥¦¦¤¢¢¢¢¢¢¡ Ÿ ¡Ÿœœžžœ››ššš™˜˜—˜™—–—˜™™˜˜—––————˜—––•””””””“’‘‘‘‘’“’‘‘‘‘‘ŽŽŽŽŽŒŽŒ‹ŒŽŒŒŒŒŒ‹‹‹‹‹‰ˆˆˆˆ‡‡ˆ‰‡‡‡‰ˆ‡‡‡‡†‡ˆ‰ˆ††‡ˆ‡‡‡‡†…„ƒƒ„ƒƒ…†……„ƒƒ„„ƒƒ„††…ƒƒƒƒ„‚ƒ……„ƒ„……„‚‚ƒ‚‚ƒƒƒ‚‚€~~€€€~}~~}||{||||}€(*-/00.*#/Ibx{~€~||zxupeM1&;Tny„ˆŒŽŠˆˆ‰‰ˆˆ‹ŽŽŒŒ‹Š‰ˆˆ‰‹ŽŒ‹ŒŽ‘Ž‘’‘’“‘Ž‘’’““‘“”‘ŽŽ‘“”“‘‘’’‘‘’ŽŽ‘‘ŒŽ’’’‘’’‘ŽŒŠ‰Š‹ŠŠ‹‹‹‹ŠŠ‹Œ‹‡nssvx|ƒ…„…„“‡V2‘‚‚}||||{ƒk,=KMF;+!37.(#%*,0*&(&$=ID.#FGF¨»¨¤À¶Tdlkjjjja.E{°Ç¤£¢¤¦¦§¨¨¦¦¦¤¢¢¢¢¢¡ ŸŸŸŸ›œœžœ›š™™™™šš™˜˜™™—•–˜˜˜————––••—˜—–––••”““““’’’‘Ž‘’’‘‘‘ŽŽŽŽŽŽŽŽŽŒŒŒŽŒŒŒ‹Š‰Š‹‹Š‰ˆˆˆˆˆ‰Š‰‡‡ˆ‰‡…†††‡‡ˆ†……†††……††…„ƒƒƒƒ„…………„ƒƒ„…„„…‡†„‚‚ƒ‚ƒƒƒ„„„ƒƒƒƒƒƒ€‚ƒ‚‚ƒƒ‚‚€€~~€€~~~}}|{{|||}~0011354/($!.G`vz|~~}|{yxuocJ/+AXl{ƒ†‰Š‹ŽŒ‰‡‡‡‡‰‹ŽŽ‹‰‰‰ˆ‡ˆŠŒŒŽŽ‘‘‘“’ŽŽŽŽ‘’‘‘‘‘‘“””“‘‘‘Ž‘ŽŽŽ’’‘‘ŽŽŒ‹‹Šˆ‡ˆŠŠ‹‹Š‹‹ŒŒttsvy}ƒ…„†‡‘Œ]$ƒˆ~€€~~}|{c#2BFA9)'.*&%&(+2. &%"8EA/!!2¡¹£¢¸°Icopnkhfef7H¯ÂÄ·¦£¤¥¦§§¥¥¤££¢¢¢¡ Ÿžœ›œœœœœœ›››™˜˜˜˜˜™™˜—˜™™˜–•–——––—–•””•–—•••––•”“““““““’‘‘ŽŽ‘‘‘‘ŽŒŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒ‹Š‰‰Š‹‹‹Š‰ˆ‰ˆ‡ˆŠŠˆ‡‡ˆˆ‡‡‡ˆˆˆ‡…„……„ƒ„„†††„„„ƒƒ„……††„ƒƒ„††……††„ƒ„„„ƒƒƒƒƒƒ„ƒ‚‚‚ƒ‚ƒƒ‚‚ƒƒ‚€€~€€€€~||}}||}||}}~~~46645882+'#.F_wz~~|{|{ywwtm`F,!1J]s}‚„…‡ˆŠŒŠŠ‹Œ‰‡†‡‰Š‹ŒŠ‰ŠŠ‰‡ˆˆŠŽŽ‘‘ŽŒŒŽŽŒŒ’“’’‘‘“”“‘‘‘ŽŽ‘’‘‘ŽˆŠ‹‹ŠŠ‹‰ˆ‹ŒŒ‹ŠŠ‹Œ–ytrtv{ƒ…„…†ˆŒbw€~€€€~||zuU$4@C:& #$#$&)0.# /=<+<©¾¨¡¹®A`qsplgecefC?ZŽ«±«¥¥§¨¨¦¤¤¤£££¢¡ Ÿžœœ›œœ›œœœœ›š™˜˜˜˜˜—˜˜˜˜™™˜˜—•”—˜—–—–•””•—˜–””•–”“““’“““’‘‘‘‘‘Ž‘‘ŽŒŒŒŒŒŽŽ‹Š‰ŠŒŒ‹‹ŒŒŒŠ‰Š‰‰‰‰Š‰ˆˆ‰ˆ†…ˆ‰‡…†‡ˆˆˆ‡ˆ‰‰‡…„…†„ƒ‚ƒ…†„ƒ„„ƒƒƒ„………„ƒƒƒ…††…„…„ƒ„…„„‚‚ƒƒƒƒƒƒ‚‚‚ƒƒ‚‚ƒƒ‚€~€€€~}}}~~}}}}||||||47754671,'!.F_uz~}zxyyvuurk^B)$7Rg}~ƒ††††ˆŠ‹ˆ‡ˆŠˆ‡‡‰‹‹‹Œ‹‹ŠˆˆŠ‹‰ˆ‡†ˆŽ‹ŠŒŽŽŽŽŽŒŠŠŒŽŒŽŽŽ‘’’‘ŽŒ‘‘‘Ž‘‘ŽŽ‘‘ŽŽŒŠ‹Œˆ‰‹‹ŠŒŽŽ‹‰‰‰‹Œ“uroqsy‚„ƒ„„…šr"t‚‚€~|||}sU"1=5!! ")*$&&¢ºª£½°D_rtplhedhkn_D^ƒ–œšž¢ª§¥£¤¤¤¤£¡ Ÿžžœœ›šš›››œš˜—˜™™—–—˜—–˜˜˜——”“–˜————–•””•–•““”•”“’‘‘“’‘Ž‘‘‘‘‘ŽŽŽŽŒ‹Œ‹Š‰‰‹Œ‹ŒŒ‹ŽŒ‹Š‰‰‰‰‰‰ˆ‰‰ˆ†…†ˆ‡……‡‰‰‰ˆˆ‰‰ˆ††††…„ƒƒ„…„ƒ„„ƒƒƒƒƒƒƒƒƒ‚‚ƒ„„ƒ‚ƒ„ƒƒ„…„ƒƒƒƒ‚‚‚€‚‚‚ƒ‚‚‚‚‚€€~}|}~~~}~}|}}{{{/464221.+'!.G_ty|{wvwwuttqiZ>' (;Vj}ƒ‡ˆ‡‡ˆŠŠ‡†‡‰‰ˆˆ‹ŽŒ‹‰††‰‹‹Š‰‡ˆ‹ŽŽŒŠ‰ŒŽŒ‹ŒŒ‹ŽŽŒŒŽŽŽŽ‘’’‘‘’ŽŒŽŽŽŽŒŠ‰‹Œ‹ˆˆŠ‹‹‹Œ‹ˆ‡ˆ‰Šmnmqtx‚„„„–}r‚ƒ€}}}{pU*' "!¡¿¸ÀÉ´GGswsmihhkpsswyuƒ‘“—ž¬®¥¤¤¤££¢¡ žžŸ žœ›š™™šššš˜˜™š™—–——–•–—–––•“•—————–•””••”“’”””’‘‘’ŽŽŽ‘‘‘ŽŒŽ‹‹ŒŒ‹Š‰‰Š‹‹ŽŒŠŒŒŒ‹‹‹Š‰‰‰‰‰†††‡‡†„…ˆ‰‰‰‰‡‡‰‰‡‡ˆ‡‡†„ƒƒ„„ƒƒ‚‚‚ƒƒ‚‚ƒƒ‚‚ƒƒƒ€‚„ƒ‚„……ƒ‚‚€€‚‚‚€~~€}{{|}}}|}}||{zyz*.11/..--("0JauxzzwvxxwvvrhW<%!*=Wk|€ƒ†ˆ‰ˆˆ‰‰‡‡‰‹Œ‹Š‹Œ‹ˆ†…ˆŒŽ‹‰ŠŒŒ‹‹ŒŒŒ‹ŠŠŒŠ‰‹ŽŽŽŒ‹‹‹‹ŠŠ‹ŽŽŽ‘ŒŒŒ‘’ŒŽŽŽŒ‹‰‰Š‹ŠŠ‰ˆŠŠŠŠŠŠŠŠ‡††ˆˆ‹oonrvy|„…„‚™Œo‹€€€~~~|mNŸ¼¶¾Á¤S]xsqmifflp~Š…‡‘‘††«©¢¢¡¡¡¢¡ŸžŸ¡¡Ÿžœ›››š™™š™™™™˜˜™˜–—˜—••–—–•–”’“–————–•”“””“’““”“’‘‘‘Ž‘Ž‘ŒŽŒŒŠŠŒŽŽ‹ŠŠ‹‹‹‹‹ŒŠ‰Š‹ŒŒ‹‹ŒŠˆˆ‰Š‰††ˆˆ‡‡††ˆ‰ˆˆˆ‡‡‰‰‡‡ˆ‡†……ƒ‚ƒ„ƒƒƒƒƒ„„„‚ƒƒ„„‚ƒƒ‚€‚ƒ‚‚ƒ…„ƒ‚‚€€‚ƒ‚€‚ƒ‚€€€€~€}|||||}}|{{{{zzz{|"&*-.--.-("2MdqxzzxxyzyyythV;$ "-BZmz€ƒ†‰‹‰‡ˆˆ‡‡‰ŒŒ‹‹Œ‹‹Œ‹Š‰‰Š‹ŒŽŽŒ‹Š‹‹‹Š‰‹Œ‹ŠŠ‹ŽŒŒŽŽŒ‹‹Š‹ŒŒ‹Š‹‘‘‹‹Œ‹‹ŽŽŽŽŽŽ‹ŠŠŠŒŒŠ‰ŠŠŠŠŠŠŠŠŠ‡††ˆ‰rqqtwz~‚„……„˜–h‡~}~€€~~}|jGCE`qh6?gxsa`_^bsy›šž›˜“—¥°ºÀº°®°¬ª£ŸŸ ŸŸŸž›š›››ššš™™š™˜˜™—–—˜—•–—˜—––”“”•–—––––””••“’“““““’‘ŽŽŽŒŽŒŒŒŠ‰‹‹ŠŠ‹‹‹‹‹‹ŒŠˆŠŠ‹Œ‹‹‹‹Š‰‰‰‰ˆ‡‡‰ˆ‡ˆ‰ˆˆ‡†ˆ‰‡‡‰‰‡††…„„„„ƒƒ„ƒ„„„„………ƒ‚‚ƒƒ‚‚‚€€‚ƒƒƒ‚€~‚ƒƒ€‚ƒ‚€~}€~}||||{{{{{zzyz{|}%*----+&!3OesyzywwxyyyxrgU;$!! $1G_oz€ƒ…‰‹ˆ‡‡‡‡ˆ‰ŒŽŒ‰‰‰Š‹‹‰‹ŒŠ‹‹Œ‹ŒŒ‹Š‰ŠŒŒ‹ŠŠ‰ŠŠ‹‹‹ŠŠŽ‹ŠŠ‹ŒŽŽŒŽ‘‘’‘Š‹Œ‹ŽŽŽŽŽŽŽŽŽŒ‹ŒŒŒ‹ŠŠŠŠŠ‰‰‰‰ˆ‡ˆˆ‰‹rrrtvz‚ƒƒƒ„›Y{y{}~~|||{xd>*<<CR`r©±²¸¼½½¾ÀÃÃÃÅÃÀ¿¿¾¼·´µ±¬§¡Ÿšš™™™˜—–––––•–˜™˜—–•””•””•–––•”••““”“’’“““ŽŽŽŒŒŽŽŒŠ‹ŒŒ‹‹‹Š‹‹‹‹‹Š‹‹ŠŠŠŠ‹ŠŠŠŠ‰‰ˆˆ‰‰ˆ‡‰Š‰‡††ˆ‰†‡‰ˆ†……„ƒƒ„……„„„„……………„‚‚ƒƒƒƒ€€€€‚‚‚~€€‚‚‚‚‚€~~~||}|{z{{{zz{{||}#),..-*$ 4SixzywtuvvwxvqeR:$ "! &6Nht|ƒ„ˆ‹Š‰‰Š‰ŠŠŒŽ‹‰Š‹Š‡ŠŽŒŒŒŒ‹Œ‹ŠŠŒŽŒŠŠŠ‰ŠŒŒ‹‰‰ŒŒŠŠŠ‹ŽŽŒ‹ŒŽ‘‘ŒŒŽŽŽŒŽ‘ŽŽŽŽ‘Ž‹‰Š‹‰ˆ‰ŠŠˆ‡‰Š‰ŠŠ“ppqstx‚‚‚ƒŒ*Ppyz{||{{{yt[4Kjt~‘ £¥ª¬¬±··µ¶¼¿»¸¸¸·¸¹¼¼µ¨¦£›–””˜˜™™˜————–””•–––••––••”“’’“““ŽŽ‘ŽŽŒŽŒŒŒ‹ŒŒ‹Š‹ŒŒŒ‹Š‰‰‹ŒŠŠŠŠŠŠŠ‹‹Š‰ˆ‰Šˆ‡††‡‡……‡ˆ‡…„…„ƒ„††……„„………„ƒƒ€€‚ƒ‚ƒƒ‚€€€€‚€~€€€€€€€€€€€€€~~~~~||}|zyz{zyz{{{{{!(+//-*$ 2Siz{yurrtuuurm]J5#!" "+=Tmx~ƒ„†ˆ‰Š‹‹Š‹Š‹ŽŽ‹ˆˆ‰‡…‡Œ‹ŠŠŒŽŠ‰‹Œ‹ŒŽŽŒ‹‹‹Š‹Ž‹Š‹ŒŒ‰‰‰Š‹ŒŒ‹Š‰ˆ‰‹‹Œ‹‹Œ‹ŒŽŽŒ‹ŠŠˆ†‰‰‡…ˆŠŠˆ‰Š‰ˆˆˆknoppt‚‚„‹™JIjyyz{{{||yzS''SqssŒ–Ÿ¦¬«ª«°¸½¹²µ»¼»»ºººº¹¬§©§¢›–””””•”“’’‘’““Ž‘‘ŽŽŽŽ‘ŽŽŽŽŽŽ‘ŽŒŒŒŒŒŒŒ‹ŠŠ‹ŒŒ‹Š‰‰‹‹Š‰‰‰‰‰‹Œ‹Šˆ‡ˆˆˆ†††‡‡†…†ˆ‡…„„„‚„†‡‡†…„„„„ƒ‚‚‚ƒ‚ƒ‚€‚€€€€€€€€~€~~~~}}}||}|zzz{{zz{|||{ (,.-*&".Ldw{yuqqstutogWC0 !#1EYny}‚„†‡ˆŠŠŠŠ‹Š‰‰‹Œ‹ˆˆˆ‡†ˆŠŠŠ‰‰‹ŒŒ‰‡ŠŒŒ‹ŠŒŽ‹ŒŒŒ‹Š‹ŒŒ‹Š‰ˆ‰‹ŒŒŒŒ‹ŒŒ‹Š‹ŒŒŒŒŽŽŽŽŽŽŽŒŠ‰‰‡…ˆ‰†…‰Š‰‰ŠŠ‰‡ˆˆ‰ommopvƒ‚‚„Š’S#4b‚{{|{z{{wrJ$Uix‡’™Ÿ¡¤¦¨ª¬®¯®®²´²±µ¹¸²®¯ª£¡•’‘‘‘’ŽŽŽŽŒŽŽŽŽŽŽŽŒ‹ŒŒŒ‹ŠŠŠ‹ŒŒ‹ŠŠŠ‹Šˆˆ‰ˆ‡‰Š‹Šˆ†…‡‡†………†‡††‡ˆ†„ƒƒ‚€‚…†…„ƒƒƒ‚‚‚‚~€€‚€‚‚€€€~}~~€€~€~||}~~~~}|}}}}}|{|||}|zzzzzzyy{}|z'*+'#*F]sxzwsrstutoeT>- %8N_v~‚ƒƒ…ˆˆ†‡‡†ˆ‰Š‰‡ˆ‰‹‹Šˆˆˆ‰ˆˆ‰Š‰‡††‰Šˆ†‡‡ˆˆˆ‰ŒŽŽŽ‹Š‹ŽŽŽŽŽŒŒŒŠ‰ŠŒŒŒŒŒŒŒŠˆˆŠ‹ŒŽŒŠ‹Ž‹‹ŒŽŽŠŠ‹Šˆˆˆ‡†…‡ˆ‡ˆ‰‰‡‡‰ˆ‡‡‰‰‡mlnprw~‚‚‚„ˆ‰Y,"T†|||{yzzvoC"#4FWi{Ÿ¡Ÿ¢©°³±®®°²µ¶³²´µ±®§ ž ¢Ÿ”‘ŒŒŒŒŽŽŒŒŒŒŒ‹‹Œ‹‹‹‰‰ŠŠ‹‹ŠŠŠŠ‹‰‡ˆ‰ˆ‡‰‹Š‰ˆ…„‡‡†………†††‡ˆˆ†„„„‚‚„„ƒ‚€€‚‚~‚‚€€€€€€€~{}~‚€~}~~}}||}|{|}|{|}|{{yz{{zzxx|}|{#&&#*D^rwzzvtuuusndS<*(=Tcx…‡‡††ˆˆ†……„†ˆŠŠˆ‡ˆŠ‹Š‰ˆ‰‰‰‰Š‹Š‡……ˆŠˆ†††‡‡‡ˆŠ‹‹ŽŽŒ‹‹ŽŒ‹ŒŒ‹ŠŠ‹ŽŽŒŒŒŒŒ‰ˆŠ‹‹ŒŒŠ‹ŽŽŒŠ‹ŒŒ‹‰Šˆ‡ˆ‰ˆ‡‡ˆ‰‰‰Šˆ‡‡‡…„…ˆˆ…mmosuy~€€ƒ„‚‰g:HŒ‚}|{yxwtm9>Q]o€‰šŸ˜”š¡¨¬©¨¬«ª«¬¯°«§§ª¥›‹‹Ž‹ŒŒŠ‰ŠŠ‹ŒŠ‰‰Š‹‹Š‰ŠŠŠ‰ˆˆˆˆˆŠŠ‰Š‰†…†ˆ‡†……††††‡‡…„„„ƒƒƒ„‚‚€€‚‚‚€ƒ„ƒ‚‚‚€€|~€€‚€~}€~€€~|{|}|{|||{||{zzyy|{{zxx|}|{ *D^twyxutuuspmeT=)+@Xgx„‹Šˆ‡†…„ƒƒƒƒ†‰Š‰‰ˆˆˆˆ‡†‡ˆˆˆ‰‹Š‰ˆˆŠ‹‰ˆ‡ˆ‰‰‡‡ˆ‰ˆŠŒŽŽŒŽŽŽŽ‹‹‹‰‡ˆ‹‹ŠŠŠŠ‹ŒŒŒ‹‹‹Š‹‹‹Š‰‰‰Š‰‰‰‰‰ŠŒŒŠ‰Š‹‹ŒŒŠˆˆ‡‡‰Š‰ŠŠ‰ˆˆ‡‡‡‡†„ƒ„…‡‡‹nlosvy}~~„…ƒ‰rJ+‡„}{{zxvsn0/QZ`ly‹˜—–š¡¨«©ª««ª¬²³¨¨§¢¢Ÿœˆ‰‰ˆ‡†‡‡‡‡‡‡‡ˆˆ‰‰‰ˆ†…†ˆˆ‡…†‡†……†…„………ƒ„…„‚‚ƒ‚‚‚‚‚‚‚‚ƒ……ƒ‚‚‚‚‚‚ƒ‚€€‚€~~~€€~€}|{||{{|||||}|{{z{||{zyy{||{(B]twxwutvtqmkcR=) !.D\m{…ˆ‹‰‡„‚‚‚‚‚‚ƒ†ˆ‰‰‡†‡‡†„†ˆˆ‡ˆ‹‹Œ‹‹ŒŒ‹‰ˆŠŒ‹‰ˆ‰Šˆˆ‹ŒŒ‹ŽŒ‹Œ‹ŠŠŠ‰‡ˆŠŠŠŠ‹ŒŒŒŒŒŒŒŒ‹ŠŒŒ‰‡ˆ‰ˆˆ‰ŠŠ‹ŒŒ‹‰Š‹ŒŠˆ‡‡‡Š‹Œ‹‰ˆ‡……‡ˆ†„ƒ…††‡‰qkostx~€€‚ƒ…„‡~W€†}zyywutn&1HMbr|„Œ”šœžž ¢¤¤ Ÿ£¦¨©¦ ›š•Œ‹Š‹Š†‡‰ˆ†…†‡ˆˆ†…………„„„„„…†…„„„ƒ‚‚ƒ‚‚ƒ„„ƒ‚ƒ„„ƒ‚ƒ„…ƒ‚‚€€€€€~~~~}}~~~~}}|{{{zz{|{|}~~}|||{yyyxwyzz{'?ZrwxwuuvurokbP:'$%!!1H_n|ˆ‡‰ˆ†………„ƒƒƒƒ„†‰‰‡††‡‡…†ˆ‰ˆ‡ˆŠŒŒŒŒŒ‹‰ˆŠ‹‹‹‹‹Šˆˆ‰ˆˆˆ‡†ˆŠŒ‹‰‡ˆ‰Š‰‰Š‰‰‰‹ŒŒ‹‹Š‹ŒŒŒ‹‹Œ‹‡††‡‡ˆ‹Œ‹‹ŒŒ‹‰‰ŠŒŒ‹‰ˆ‡‡‡Š‹ŒŒ‹Š‰†„…‡ˆ‡……‡†…†mkorrw~‚ƒ‚‚„…„†a~„{xwyywtj!*/?T\bs}€†•œ¢¢¡¡ ¡§¬« ¡£›œ™•Š‹‹Šˆ‚‚ƒ„…ƒ‚‚ƒ‚‚ƒ‚‚ƒƒ€€‚ƒƒ‚ƒ„…ƒ€€‚€€€~€€€~|}~~~~~}||{{{{{{zz{{}~~}}||{yxxyxwxxyy '>[twyywvwvvsnbL5#!%$!%7Oco}ˆ‡††…†‡ˆ‡……†…„…‡ˆˆ‡‡ˆˆ‡‡‰‰ˆ††ˆŠ‹Œ‹ŒŒ‹‹‹Š‹ŒŒ‹‰ˆ‡‡ˆˆ†……ˆ‹Œ‰†‡ˆ‰ˆˆˆ‡‡‰ŒŒ‹ŒŒŽŽ‹‹‹Š‡†………†‰ŠŠ‹‹Š‹ŠŠˆ‰‰‰‰ˆ‰ŠŠŒŒŠˆ‡‡‡ˆ‡††‡†„„‰ljpsty‚ƒ‚‚„……‹isyxxyyw{o+?IS[dr‰‹’———˜——™œŸŸ• £¤š––”ŠŠ…‚ƒ‚€€~€€‚ƒ‚€~€€€}~€€~}}~~}}}|{{{zzz{{{zzz|||{zz{zyxyyxxxyyy##(A\vyyyvvwwwunaH1!"" +@Whq|††…„„…‡‡††‡ˆ†ƒ„…†‡‡†ˆˆ‡‡ˆ‡†ƒ„†ˆŠŠŠ‹ŽŽŽŒ‰ˆˆ‰ŠŠˆ††‡‰ˆ††‡ˆ‹Š‰‰‰‰ˆ‡††‡‰‹Œ‹Š‹ŽŒŒ‹ŒŒŠˆˆ‡ˆˆˆ‡‡ˆ‹ŒŒ‹‹‹‰‡†ˆŠ‰‰ˆ†…‡ŠŒ‹‰ˆˆ‡††…„„††„ƒ…fhnrtx}‚ƒƒ€Šuhywxxxvtvj18@Sagkq{Žš–—ž£™———‘”•ˆˆˆˆ†††~}}}~~}}}}~~}~~~€~}}}|{{|{zzyxxyz{|{{z{{{{z{{zzzzyxxxyyx$$ +F_xzzxvuvwvsl^F1!" #0G\js}ƒ†††…†‡ˆˆ‡‡ˆ‡………†ˆ‡†‡‡†‡ˆ‡…ƒ„…‡ŠŒŠŠ‹‹ˆ‡††‡ˆ‡†‡‰Š‰ˆŠ‰ŠŒŒŠŠŠŠ‹Š‰ˆˆˆ‰‰‰‰ŠŒŒ‹‹ŒŒŠ‰Š‹‹‰‰‹ŒŒ‹‹‹ŒŒŠ‰ˆŠ‹ŠŠŠ‡……ˆˆ‡†‡‡‡‡‡…ƒ„………ƒƒlimorw|~€‚‚‚‚cwvyzyvss].G\iszvxz|Ž†‰•–ŒŠŠ‹Œƒ……ƒ|}}~~}|||zzz{{zzxwwxyz{{zzzyz{{{{yy{zxxxxxyw/JaxyyxursttphZB0!!%5L^ks~ƒ†‡‡††‡‰‰ˆˆ‡††……†ˆ‡…†‡††ˆ‰†„„…‡Š‹Šˆ‡ˆˆˆ‡‡‡†…†‡‰Š‹‹‰ˆˆˆˆ‰ŠŠŠŠ‹‹ŒŒŠ‰ˆ‡†‡‡ˆ‰ŠŠŠŠ‰ŠŠŠˆ††ˆ‹Œ‹‰‰‹‹Š‰‰ŠŒŒ‹ŠŠŠˆ‡‡‡†„ƒƒƒƒ…†‡‡†„ƒ„…„ƒƒ‚pklmoty|~€€€‡[rvz{zwrj[#->PZ[[gswy…‰ƒ„…‡Š‹Œƒƒƒ„„……ƒzyyzzzzyyyzzyyz{zzxxyxwwxxxxw1LapvxxtqrttoeW>,':Q`kuƒ„…†…„†‡ˆ‰‰ˆ‡………††…„†‡†…‡‰ˆ…„„…‡‰Š‰‡‡ˆˆˆˆˆ‡…††ˆ‰ŠŠ‰‡†…††ˆ‰Š‹Œ‹‹ŠŠ‰‰ˆˆ‡††ˆ‰‰‰ˆ†……†ˆ‡…ƒ„‡‰‰ˆ‡ˆ‰ˆ‡ˆ‰Š‹Œ‹‰ˆˆ‡††ˆˆ„‚ƒ„…‡‰ˆ‡†††…ƒƒƒolkloswz|}~€€€‡NmyzzyvqiY*4;GSZamw{~€‚„………†ŠŒ}|{|wwxyxxyywwvvyzxwwx 4O`mtuutrstsndT<()>Tbmx„ƒ‚ƒ„ƒ‚„†ˆ‰‰‡………„ƒ‚ƒ†ˆ…ƒ…ˆˆ†…………‡‰ˆ‡‡Š‹‹ŠŠˆ‡‡†††‡‡‡†………†ˆ‰‰Š‹‹Šˆˆˆˆ‰ŠŠˆ†‡ˆˆ‡†„ƒƒ†‰ˆ†……‡ˆˆ†……†‡‡‡‡†‡‰‰†…‡‡„…‡‡„‚ƒ…†‡‰‡††‡†„‚ƒ„mjilpsvyz{~~…ŽFfwyyxuplS+DV\aekuqsƒ…†‰~|zz…yvtuw %:RcotsstuusrlbS;&*@Udq{…ƒƒ……ƒƒ†ˆŠŠ‰‡‡‡†„ƒ„‡ˆ„€‚…‡‡‡‡ˆ‡†††…†‰ŽŒŠ‰‰‰‡…„……„„…‡ˆ‰Š‰ˆ‰ŠŠŠŠ‰‡‡ˆŠ‹Šˆˆ‰ˆ‡††††‰ŠŠˆˆ‰‰ˆ‡†………††††„…ˆˆ…„‡ˆ†…†‡††…†‡‡ˆ‰ˆ†‡‡†„ƒƒƒ‚mhimqtwyyz}~~><]twxxvuhJ#3H[bcehluxx„#!.;Vgorqqrstspj^P;& ,@Tes|………†…„‚ƒ…†‡‡‡†††ˆˆ‡††‡‡…‚ƒ…‡ˆ‰‰ˆ†ƒƒƒƒ‚Š‹Š‡ˆ‹‹ˆ…………ƒ‚„‡‰Šˆ††‡‡ˆ‰‹Š‡‡†‡ˆˆˆˆ‰Š‰‰‰‰Š‹ŒŠˆ‡ˆ‰‡††‡‡‡‡……†…†ˆˆ…„…†…„ƒ…†‡ˆ‰ˆ‡‡‰‡…††„„ƒ‚‚‚nginrtxzz{}€~}ˆV 0Tpstuur_@&ej&!".>Qakopnnoptuqi\M8$ ! $1BUev}ƒ…†‡‡†……†††…„„…‡‰Š‰‡††‡†ƒƒ„…‡ˆˆˆ‰‡…„„ƒƒ‡Š‰‡ˆŠ‹‰‡‡‡‡…ƒ„…‡‰ˆ††‡†…†‰ˆ†…„„…†††‡‰ŠŠŠŠ‹ŒŒ‹ˆ‡‡ˆˆˆ‰‹ŠŠˆ†………†ˆˆ†…„„ƒƒƒ…†ˆŠ‹ˆ‡‡ˆ…„„…„…„ƒ‚ƒnfioqsvz|~~€~†\,(Ioqqssp[;-)"&.=O]iopolkknsuqiZH2!!"""!&6FXj€‚„„…………†††……„„†ˆ‰Š‰ˆ‡‡‡†„ƒ…‡ˆˆ†‡‰‰ˆ‡…„ƒ„†ˆˆˆ‡ˆ‡‡‡‡†…„„„…‡†…‡‡…ƒƒ…†„ƒ„……ƒƒ„…‡ˆ‡‡ˆˆˆ‰Š‰‡‡‰‰‰ŠŠŠŠ‰‡†…„…ˆ‡†…„ƒ‚ƒƒ…ˆŠ‰††‡†„ƒƒƒƒƒƒƒlfinpqtx{}}‡c6?qrstskX4(")49J[dmqrqmiimrtqhWC.!""#"(:L\m‚€‚ƒ„„„„……†††…„…‡‰Š‰‰‰ˆˆˆ†ƒ‚…ˆŠ‰…†‰ŠŠŠˆ†„ƒƒ†ˆ‡…………††………†…„„„…‡‡…‚ƒ…‡„„†ˆ‡„ƒ„†‡‡††‡††‡‰‰‰Š‹Šˆˆ‡ˆ‰‰‡††…†‡‡…„„ƒ‚‚ƒ‚ƒ‡‰ˆ…††„„……„ƒ‚~}zldinprtwy|}~~jC5rtuvupO-*$$+>LT`imnpsurnmorsqhU@,!! !"+>Qan}‚ƒƒƒƒƒ„…††„ƒ‚ƒ…‡ˆˆˆˆˆˆ‡…ƒ„‡‰ˆ†‡‰‰ˆ‰‡†…ƒ„‡†„„…„ƒƒ„„…†‡…‚ƒ„††„„†ˆ‡††‡‰‰†…†‡‡††……††‡ˆ‰‰‰ˆ‡†………†‡…………†……„„ƒ‚‚ƒƒ…†…„„„„„……„‚}}}zjbgmoquwxz||||}}uR)psuvuqG&+%$+4@WanosutsvwusppqrpfS>+ "-AUep‚ƒƒ‚ƒ„„„†‡†ƒ‚…††‡‡‡ˆ‡…„ƒƒ…‡‡‰‹‰ˆˆ‡††…„…†‡‡‡‡†…„…††‡ˆ†‚ƒ…†‡†‡‰Šˆ‡‡†‡ˆˆ‡ˆ‰ˆ‡‡††‡‡†‡ˆˆ‡††††…„„„„„„…†…„…†…„„……ƒƒ„ƒƒƒ„„ƒƒ„‚€€|ecfloptxz{{}~~}{}_sxssrrG")# (0;DQblpruvvuvvtssqqqodP;) #1EYgp}‚‚€ƒƒƒ†ˆ†€ƒ„„…††‡†„„„‚€€‚„‡ŠŒŠˆ‡†‡‡†…„…ˆŠ‰‡†……†‡‡‡‡„‚…†ˆˆ‡ˆ‰ˆˆ…ƒƒ††‡‰Š‰‡†……†‡…„…„„„……………„ƒƒƒƒ„„ƒƒ…‡…„…††…„‚„ƒ‚€~kdchnqrvzzz}~z‡jfqopnnC##,6@LU_lsttuvvvutssrqqpl`K8'!!&6K\hoz€ƒ„„†ˆ†€‚‚‚ƒ„„…†………†„‚‚„†‰Šˆ†…‡ˆ‡…„†‰Š‰ˆ‡……†‡‡ˆ‡…ƒƒ„…‡‰ˆ‡†‡‡†ƒ‚„…ˆ‹‹Šˆ†…††‡…„ƒ‚„††………†……„„„„„„…‡ˆ…„†ˆ†…‚ƒ€ƒ„‚€~~}}mgehostvxyy}}zŠrYhnomi9 '1<HR[`gpuvvuuutsqqqpoonj\E3& "!'9P_jsz}~€‚‚ƒ…††…€€€€‚„„„„„……„ƒ‚‚…‡†„ƒ†‡†……‡ˆ‡‡†…„„„………………„‚ƒ…†…‚‚„‡‡„ƒƒ„‡Š‹Šˆ†……„„„ƒ…ˆ†…„„…………„„„ƒƒ…†…ƒ‚„…„‚‚‚‚‚‚ƒ‚‚}|{{zjfehmqsuwxz}€|{zTfnpnb3 )4=MXagjlptwxxwvtrqpponmkfX@/$ "!*<Salu{|}‚‚„…†„‚€ƒ„ƒƒ„„…†…„‚€€‚„……„…††‡ˆ‰ˆ‡†…„„„„ƒ‚ƒ„„„„‚‚ƒ„‚€ƒ…†…ƒƒ„‡‰Š‹ˆ††…ƒƒ„ƒ‚‚†‰ˆ‡†…………†…„ƒƒ„…„ƒ‚„ƒ‚ƒ…„ƒƒ„ƒ‚‚}|{nfgknpsvxz{|}~}|ƒMcnqpn, &0<HW`gknpqqrtvxwuttsrpoomidW@.#!!!-?Udnv}~~‚€‚„„ƒ‚‚€~~€‚ƒƒ„„„‚€€ƒ…††…„„†ˆ‰‡…„‚ƒƒ‚ƒ„ƒ‚‚‚‚ƒ……ƒ‚ƒ…††††…†…„ƒƒ‚‚ƒ…††‡†„ƒƒ„„„ƒ‚„„‚€€‚ƒ…†„ƒƒƒ‚€~€~~oghlpqqtx{{z{{||ƒAalqqm$&-7CS^ksqrsuvsrrsuvutstrpnnmhaU?."!! $/AXgpw}~~~€‚}‚ƒ‚‚ƒ„„‚€‚ƒ„ƒ‚€€‚ƒƒ„…‡‡†…„…‡‡‡…„‚€ƒ„ƒ‚ƒƒ‚‚ƒ„ƒ‚‚„…„ƒ„„„„……†‡ˆˆ‡†……………†‡…ƒƒ„ƒƒ„„‚ƒ……„ƒƒ‚‚ƒ„…†…„ƒƒƒ‚~~}znegmstrsw|{zz{||zƒ+0Vjpng!)3?IT_gntuuvxwtqpqstusrrqnmmmi`T?-!! %1C[krx|~~~€‚€}~ƒ‚‚„…„„‚€€€‚ƒƒ€‚ƒ…†………„„„‚‚ƒ„„„‚„„ƒ‚ƒ„……„ƒ‚„†…ƒƒ„……„ƒ„„ƒ…†…„…………„ƒ„„„…††„ƒ‚‚‚‚ƒƒ‚„‡‡†ƒ‚ƒ„„ƒ„„ƒƒƒ‚~}}{{|||wlbbjrtrsvzyxy{||z€A+Timi`$-8EPYagkntvwxyxuqppqrsssrqommokaS=+%3I_muz~~~€‚‚‚ƒƒ„……‚€€ƒ„…„‚ƒ„…†‡†…ƒ‚‚ƒ€€‚‚ƒƒ‚‚ƒ„ƒ‚‚„†‡ˆ†…„…‡†„„„ƒ‚ƒ…ƒƒ…†…„„„„„ƒƒƒƒ„…‡‡…„ƒ‚‚‚„ˆˆ‡ƒ€„†…ƒƒ„…„ƒ}}}|zz{{{{kaairuuuvxxwyzz{zzO%$OgkeX '0=LW_dfhjmpruwxvtpnnoopstsrpmmnj^O:( (8Ocnvz~€~€ƒƒ‚€€ƒƒ€€ƒ…„‚€€‚‚ƒƒƒ„…„€€~~€‚ƒƒ‚‚‚‚ƒƒ„…„„„…†…ƒ€„„ƒƒ„„ƒƒƒ‚ƒƒ‚ƒƒ‚‚ƒƒƒƒƒ€‚€€„††ƒ‚„†…ƒƒ„„‚€}{{||{zzzzvkb`hqtstuvwxz{{|{sX3<mjcS$-6AP]fjkkkllmosvvuspnmmmorssrqnmmfYI5$ +>Udovz€~}‚€~~€‚ƒ€„†„€‚‚‚„„‚€€€€€€‚…„ƒ€‚ƒƒƒƒ……ƒ€‚„…ƒƒ„„„‚‚‚‚‚‚ƒ‚€‚ƒ…„€€‚…ƒ€ƒ„„ƒƒ…†…‚ƒƒ‚€}{{}}{{zyyvledjrsrsuwy|}~}|zsbA+mj`O!*5AJR\gklmnnmklosssssrqpmmnoqqrpmjiaRC/ -CXeoty}~{{~€€€}}~€‚‚€…†ƒ€‚‚‚€€‚ƒ€‚‚~‚…„‚€€€€‚ƒ‚€‚………‚„…„ƒ€€‚ƒ‚‚ƒƒ„ƒ€ƒ…„€€€‚ƒ‚‚ƒƒ~€€€}||zxyzzzukcdkpqpqtwxyyxxxvpjK nh]M$/;GQY_flnnpqpommpqqopsttrmlmnnoonjge]N?,0I]lsx{{z~~‚‚}}~†‡ƒ‚ƒ‚€~~€‚‚‚ƒƒƒ„„‚……ƒ„ƒ‚‚‚‚‚‚ƒƒ‚ƒ………‚ƒ……ƒ‚„ƒ‚‚ƒƒƒƒƒƒ‚‚ƒƒ}~€€€‚ƒ‚~~}}~~}{yvvy|z|j][gmnknƒˆyƒxvrstphSjeYH)3@KTZ`dglnnprrpnnqrqnortusnmnnmmmlifd[M>-! 3N`mtx|||€€€€ƒ~~~~}{|}€……ƒ‚ƒƒ€~~~€‚„„ƒƒƒƒ‚€€‚ƒ„„‚‚ƒ„„„‚‚ƒƒ‚ƒƒƒ‚ƒ„„…ƒƒ„„ƒ€‚ƒ‚ƒƒ‚‚‚‚ƒ€€€€€~~€‚ƒ‚~}|{|}}{}ysqzŠ‰kK94456:KTZgslsyolf[eaU9#,8BMV]beghknoqrrpnpqrsrqrtusonnnmllmmkf\K=.""6Oanvz{}~|}ƒƒ‚‚‚~~}||~€‚ƒƒƒ„ƒ}}€‚€~~€ƒ†…„„„‚€€‚‚€‚‚‚ƒ„…††‡„‚ƒ„…ƒ‚‚ƒ„ƒ‚‚‚‚‚‚ƒ€‚„ƒ‚‚‚€~€€€€€€€}}|{y{~yplgmm_A#'Hccjqp\]Q2%/;GNT[`cfijlmnpqpnnppprsrrssrpppomlmopmf\I9,!';N`nvzz{}}~€‚„„‚€~}~~~€€€€‚€€}|~ƒ††……„€‚ƒ€€€‚‚ƒ„†„~€‚„…ƒ€‚ƒ‚‚‚€‚‚€}€„ƒ€~~€€~~€€€€~~}|{|vui_WJ3.QXL/#+3>JTY]adfhijkkmoonnnoooprsstsqpqrpnmmnomf[F4).AQaox{{}€€€‚‚€||}~~€‚ƒ‚€}}~€‚‚€~|ƒ…†……„ƒƒ„ƒ‚‚€ƒ…„€‚„…†„‚€‚‚„ƒ‚}‚ƒƒ‚€€ƒ„ƒ‚‚~}~€€~}~~~~~ƒ‚zg\RE5!KPG,#-9CLU\`bdegiiiijlmllnoonmnpssssqpqrpmlljlkcWA0% 3GXepx{}€~|}~~}}}{z{|}~€‚ƒ‚}||}~€‚‚‚€~|‚‚ƒƒ……„‚‚ƒƒƒƒ„ƒ€€‚‚€‚ƒ„„„‚€€€ƒ…„ƒƒ‚€€‚ƒƒ‚‚‚ƒƒƒ€‚‚}}~€€}|~~|||„udWC1&#(73#!-:FQ[afiihgjjhghikmlmoponklostsrqppqokjigii`R=-$!5K]iry|}€}{{{{z||{{|~~€‚€~~~€€‚‚€~~€€€€‚„„„ƒ‚ƒ„„„……€€€€€€€€€ƒƒ‚€€€€ƒ……„„„‚‚‚‚‚ƒ‚‚‚‚€€‚‚€‚‚}~~}zy|€v^I6#(5COYbfjkjijkkhhikmnnpqrqolkmqssqqpppnjhhhjh]N;+#"6Nakrxz||}|{{{|{z{{{|~}}~€€~~€€€€€€~€~€‚‚‚‚€‚ƒƒ‚‚€€€~~€€‚‚€€€€€‚ƒƒƒƒ‚‚‚‚€€€€~~‚‚€~||~~}zw{oP6$!&-$2BPX`fhjkkkkkjijklnopqqrrpnlmortrppoonkiiijfZJ8)!%9Qblswz|||||||}}{|||{{€}}~€‚‚€€€~~€‚ƒƒ‚‚€‚ƒ‚‚‚€~~‚‚‚ƒ„„‚€€‚€€€€€~~€€~~€‚€~}||}~~}rfA("'*+)$%1?NX_befhjklkkkjkllmoppnopomllnpsrqponmlkjihcWI6(!(<Scnty{}~~}~~}}|{~€{y|€~}||}€‚‚€€}~‚‚€‚ƒƒ‚€}}€‚€‚‚‚‚‚€‚ƒ~~~}}|}~€€}}~|}€~~|{{z{|‚gU'"+3;BGKNPZO;&"*/9CNY_ceeegikkjklkkklnpqqnmonnnnmnpqsrokhhhhgf`UG6(!!*>Vgpw}~~‚ƒ|~~€~~€€~}€€~}|}~€€€€€€€€€€‚ƒƒƒ~|€‚€€‚‚‚‚‚‚‚‚~}}~~}}~~~~~€~~€~~||zz~‚r=, '*)%!")06>GPW]_]ZSH9*=CKRX]adffgghhhhjkjiikmpqpnmnnoqpmlmosspkhhiigd^SB4( $/AYjrx|}}}~€€}z|}|~~~~€€€€~~|{}}}}}}~€€~}~€‚ƒ‚€~}}}{|~~€€€€‚ƒ€€ƒƒ~|~~€‚~~~}}€€€€€~~~~||{ywl^.<ADD@8-$ !$').9CMSVXXWOB5&LPW]`bcehiijihgfgiiggikmnomkkmpsrpmlnrsqnmmlkhd\N</&%6K]lsw{||{}~|yyzzy{}}}}~~|}||~€~||||}€€~}}ƒ„„~}}~€€~|{}€€€ƒƒ‚‚‚€€ƒ‚~|~€ƒ„ƒ€€‚€~||z{sG/2MV[[ZTJ;-&%&%%&+4=DHIIID:0$UY_cddehjjhhihgeehhfeghhjlkhhkoqqqonnpppooonmje\J8,$%:Qamsvyyyz}}{xxzywx{{z{{zxy}~~|{}}}||{{|}~}{{|}€ƒƒƒ€}|}€~|}€€€‚‚‚€€€||~€‚ƒ‚~}~€€€‚‚€€€€€€~~}}{{…p*MrkmijifaWH7..-)&&(,28<<;71*"Z]cfffgijhedghgddgfcdfggjljhilmnnnljikmnooonljeZH5+$'=Tdosvxvvz~~|zz{{xwz{zz{|yx}€~~}||||}|}~~|{{|{{€ƒ‚‚€~€€~}}~€€€‚‚‚‚‚‚€€€€}}€€€€}|}~€€‚€€€‚€~}xokV _pxrrsssqi`TB9982-***-0/-+& "[^cffefhhfbaegfbdfdbdeghkliikllkjkifegkmnmkkjje[G5*%)>Tdotxxuty~~|{{||yvy{zz|~|y|€€}|z{|}|}~|yyz{zz~‚€‚~|{{}}€€€€€~~~}}€}||}~}}~~~‚€€€€|teVG#!,5gvtvvvwwumdYH@BA<3.-++,*&$ # yuyuxtwtvtuuuvtvvuwtwtvvtwtwvvxtytvuuwuwvwvvuwuxuxvyvyuwuwvvvwvwvwxyxywyvzuztytwuvuvtyt{t{t{tzuzu{t|t|t|u{uzuytxtxtytzt{t}r~r}s|u{t}}€€€~~~}~||{|z}xu€s‚r„q…r„r†p†nˆlŠj‹hd`Ž_ŽaŒ`Š`‰`‹b‹i‡tz|mos|u|u{t|t}t~|€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~~~~~~~~~}~}}||||{{zxtrrstvvwuysxsysyryqyqyqyrxswswuwwvwvwvvxvyvzvzvyvxvwvvvwsvrvqvqvsvtwtxtxsxtvuxvxvyvxtxsututvvvvxvxvwuvvvvvwvwvxtxwvwuxvxwxxwwwvwtxsvssuswtxvwvwuyuzuztytytyuwuwvvwwxyyyxzv|t}t}uzwwvvtxtytxvxwxwwvxvyuyuwvvuxt{r}q|r{r{s|s~st}t|uzr{{~€€€~~}}}}}|}{}w}u~s€p‚p†o‡pˆo‡o…m…k†j‰h‹e`__‹`‰aˆaˆdŠfŒjˆuy~j‚m~sztyvzv}t|€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~~~~~~~~~~~}~}|zyxyxzyzuulxlyqzsztztzuzs{pzoxoxpxqyryrxswsvtwuyvzvztytxuyvxvxuxrvpvpvpurutxtyuxvwvuwvuwuyuxvwvvuvvwwvywyxvxtytxvwxvxvxvywywwxwzvzvzuwvvuwuvtvuuyu{uzvyuyuzuzu{u{u{uzuyuxvxwwxwwwuzuzu{uywxvyvzuzuxvwvwvwuyuytyuwvwvxu{t~t~t}t{s|r}s}s}t|r}s|}}€€~~~}~}{~y~vtqƒo„m†lˆm‰kŠj‹jŒjŠi‰hˆeŠb‰`‰_‰_ˆ_ˆ_ˆaˆbŠc‹e‹n‚rvuuvuuvyv}u|€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~~~~~~~~~~~}}|zyvwvwzyzwukxnzs|tzsyszszrzqypyowqwqyqzo{oyqxtxuztytyswuwuxuxtxtwsvtvsvrwsxtyuxuwuvvvwwwxwxwxyxywxxvytxtxswsxsyuwxvxvzwzvzwzvywxyuytytxvvwuxuwvwvwvxwxuwuxuyuzu{u{u{u{tytyuyuwwwwvwwwwvwwxwyuzu{t{tytxtwvxuzu{uzuzuyvyu{v|v|u|uzt{s}r~r}s|s{tzt|~€€~~|~|~z~w~tr‚o†mˆlˆk‡iŠjihhhŽf‹dˆb†a„`„_†`ˆ`ˆb‡b‰b‹ddl€osxoyryt|t|€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€|z||}}~}~~~~~~~~~}}|{yxvwvzxzxuowrzvzuytzq|n{pzqysxswtxsysyryryszvyvyuxtxtvuuuuuutuuuuvuwuxtytztytxuwuwvxwxxxxxxwxwzvzvyxuysxqwrwtvuvvvwvxwzvzwywywywyvyvxvxvyvyuztyuwvvwuvvvvuyuyu{v{vzvyuxuyvyvwwwwwuxuyuyuyuzuytzszs{s|s|u{vzwywyvyvyuyvyvzuzu{uzuzt{szsztyuyu{r|z~~~}}||{{y|v~t€q„mˆkˆi‰g‡h‡iŠgfeŽedŒc‰b‡b…a†a‰b‹`Š`Š`‹aŽcŽc‹fƒouxn{s{t|s}{€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€ywyx{z|{}|}|~}~~~~~}}}|{zy{z|{|zxrwk|i}i}i~h€de~k{q{t{uzuwvwvwuytztyvxwwwwwvwtvtvsusvtuuuvuxuxvxwwvuvuvvvxuyuwuvwuwvxvzwywxxvxuvuvvtvtwtvtvvwwwwvvvuvuwuwtvttusvsvutxtxuxvwvwtxsytytztztztytxuyuzu{uzuyuyuyt{u{u|u|t{sytytzt|t|t{uyuytyt{sztytytzt{t{uyuytyszr{r|r~r‚{‚€~~~}|}z|x{v}tq„m†i†g‡e‡e‡f‰e‹dbbŽbŽcŽbŽaaaŒ`Œ`Œ^\Ž\]Ž`Œa‰c‡jt{yw{t}|€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€yxyxzxyxzx{z||}}}}~~~~}|||}|}}}|{xzr{o|p~p~ol~i}i|qxvyvxvwwvxvwxtytytwvvxvxwwwvvwuvtvtusutuvvwuyuwuvuuuvuvuvuvwuyuyvyxwywyvwvwuvuuttutwsytzvyvxvvuvuvvvvvuvuuutvtvuuxuyvzv{v{uzuyvyu{t|r|r{tyuxuytzsyszs{t{szuxwyv{v|tztytwsxs{s|s|t|r|q{qzswtvuwtyuzuzuyuzs|p}p}o}s|u|~}|zx~w~vtƒp†m†hˆe‰c‹bŠcŠd‹ddŒdccb‘`_Ž^Œ^‹_‹^Œ]\\]Œ_‹aŠf„k}v{xz}~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€}||{zxywxvyvzx{z||~}~~~}}}}~}}||{}|~~~~~~~{vuuvvuwtysyvyywyvxvwwwwvxvwuxsysyuxxxywvuuvuvuwuxtxryrxtxuwwvyvzv{vywxxwxvyuwtvtutututvuvwvzvyvyuwuwtvuuuuuuvvvuvuxuxxwzuzuzvzvyuzuyu{t|szsztxuvuwswqxq|s~t|vzvvwwv{u|t|uzuxuxtxtys{t{q{q{pzrysxuxuzu{vzvyxyvyt{r{syuwtwz|~|~{zx}w}vt‚o…lˆi‹fŒd‹cŠd‰eŠf‹fŒcŽbŽa`‘`^Œ]Š[‰[‹[Œ[\Œ]Œ\‹_ŠbŠbŠi†s€z}}~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~}|{zxyvxvxvxwzy|{}{||}|}|}||{~}€€€€€€€€~wvuuwtwtxuxwxywyvxwxwwwvwvvtvtwuxwzyzyyvwtwswtvuvuwtwuwuvuvvuxuxvvwtytwuwvwvwuvsvsvrwsvtuvtxtxtwuwtwtvtututwuxuwvuwtwuxvwvuvtwtwtwtvtvtwuwvyvwwvwtutsvrzs|v{wvxtvvvyt{t{t{u{u{uytxrxrzqzqzrxsxtxtxrzs{t|v|v{vzuzt{s{s{t{|}~}}{|z{w{u}t€qƒm…k‰jŠfŒd‹bŠbˆc‰b‹a`__Ž`Ž_Ž^Œ\‹ZŒYŽ[[\]^Œ`Œbbg‹n‚y~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~}|{yywxvwuwvxvyw|z|{}|}|~~€€€€€€€€~xwvuxvyvyxywyuxuxvxvxvwuvuututuuuvwvxvxtyryrzsyuxvxwvwvvtusvswtwvuxrxrxuvxvzvyuwuuuswswtvvuvuvuuuwtxtyuxuyuyuzuwwvwvwvxvytxswrwqvrvrustvuwvxvwvvvtvuuwtzt|uzuxuwuxt{s{szsztzt{tzsyszs|r|s|t{u{v{vzt{s|s}t|u|uzuyt{s|s}s{ty}}~}||z}w€tƒq…m‡l†k‡i‡e‡c‡aŠa‰aŠ`Œ_Ž__Š_‰_‹^Ž]\[‘[‘]^a`‹`‹_Œ^Ž_eŠq{{~}€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~|{yxuvsurvtxvyxzy{{}}€€€€€€€€{wxuyvxvzv{uztytwwwwwvwtwtvuuttusvtvvuwtxtwuyuywywxxxvxvvvvvvvwvxtxtxtxvwwvxuytytwuuxtxtxuwuvvvvwwwwwxwywzvyvywwwvwvvvwwyuytyrxrwswvvwuwwxxxxwvwtwsytzu|u|v|u|w{wzvzuzsysxtxtzu{t|t|t|s}s}s|t{v{v{v|t|s}s{tyuxuxt{r}rtq}z}~{|x}u€r‚qƒn„k„i…f†dˆcŒbŽbŽbŽbŽaŽaŒaŠ_Š]‹\]Ž]Ž_`aŽ`Œ`Œ`‹^Œ`Œ^`d‰n…t~y}~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~}|{yyvwsuqurvtwvzy~~~~{vysysxtytytxtwuwvvvvuwuwuvuvvuvtuuuvtvswtvuvwwxwywxxuxsvsvuwvwwxvxuytzryrwsvuvwwwxvyuxuwtvutuuuuuwuxvwwwwvwvvvuvtvuxvxxxxxywwvvvuvvvwvwywzwzwxxvyszt|u|v|v{u{uzvxwxvxuxuvuxvzvzvzuxtys{t|szuxvzv{v|v|u{tzsysyszq}o~qtu€|~~{{xzu{s{r}o€niec…aŠ```Ž`aŒa`_‹^‹]‹^Š]‹^Œ_`aŒ^Œ]‹]‹`Š`‰aŠ`‰e„l}uy~~~~|{ywvsurtqxw}}zvwqyrysysytxtxtxuxvxuxuxtxtwtwtvtwswsvrwswtxuxvvvvwvvvuvtvuvvwwxwxvyvzuyuxwwwwwwwwvwuwtvtuuututusutuwuwuxtxuvuuvtwvxvyvyvxvwvvwuvuwtxvxxxzyzyxxvxtys{t{uyuwtxtytyuyuxtwtwtyu{u{uzuwvxtzt|t|u{v|w|v|vztyryqzq{r}rrsu~w}{~~|{xyw{t}q€oƒl„jƒe‚a…^‰]‹_Œ_Œ_Š_‹^^Ž^`‹_‰_Š^^___[[‹^Š^‹^Œ`^Œd‰oyz~€€€€€€€€€€€€€€€€€€€€€€€€~~|{{xzw{{~~€€€€€€€€~~zvvrxsxsyszt{t{tzuyvyvyuxtxuwvvuwuxtxuwtwuwtwuvuuuuvvvvvwvwuwtxuzvzvzvxwxwwwwvwuvuvvvvvvvvwuwtwtustutwszt{uywvwtvtvvuwvwwvvvvwtxtxtytzvyw{w{w|vztyrzqzsytwuxuyu|t|s{sysxtwuyt{s|r}r|q|r|s}s|t|t{u|t|uytxsxsyr|s}s~r~st~s~w}~}{zvyu{r€o…m‰k‹i‹g‰b‰_‰^‹_‹_‹^Š]Š\Œ\Ž^_Ž^Œ^Œ^Ž_Ž`bŽ`_Ž^ŽaabŽeŽcŽfq„||€~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~€€€€€€€€~~wurswtvvvvvwxv{s{s{szuyvxvvtwtwtxuxvywxwwvwvvuwtxsxsxuwuwuwsxswuvwvwvxvxxwxvxvwwvvvuuuvwuyvzuzuxuwtutwtxtyvyvxvwvwvvvvwwwxwxuvtwtxuyvwvvvwuwuys{r}r|rztyvwvwwwvyv{u|u|u{uyuzu|t|s}t{r{s|r|p|q{qzsztzuytysyszr{r}r|r|r|s}q{w|~~z|v{r}o‚m†lŠkŒgŽebŒa‹_Š^‹\]]‹\Œ[[\\Ž\Ž[Ž[]``‘b_aŽbbbbŒcŒl…w~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€ywsuwwvxvywxwwxtxryqyrxsxrwrwrvuwwvwvwwvwuxuwtxsyryqyrxswtxtxtwvvvuwuvwuxuxuxvxwwwwvvwvzu|u}v}v{vyvxvvtvtvsxsytzvzwwwxwyvywwvuvtvuuvtvtvsvtvtwsxsztzsytytxuxvxuxvxv{v{u{s{s{s|u|t|t}s}q}r{q{rzsztytytyt{s{q|q|q|szszr|p~l{x|~|~wroƒn„m‡lŠhfdŒcŠa‰^Œ]Ž]Ž^‹]‰\Š\Ž\]^Ž^Ž_Ž`Ž`Ž`Ža^_aa^_Œbj‰uƒ~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~~~~€~€~€~~~~€€€€€€€~{xxxxxwwwvwvwwwvvuvtvuvuvvvvvvwvxwwwwxwwxwxwxvxuxsxqxpxqxsxtxwwxvvuvttttvtvuwuxuwuxuxvwxxyv{wzwzwywvvsvsustwswtxvxvwvyvyvwwvwuvvuwtvsxuzu|u{vyuwuwtxsytysytxuxvwwvvyuzu|s|s{rzszs{r|r|q}s{t{s{s|s|t{t{t|t}q~rp~r}s|r~rozy|~zu…p‡n‡m…l„k‡hŠfŒdŒcŠaŠ_Œ]Ž]Ž]Œ^Š_‰`Œ_Ž^]_`bbŽaŒaŒ_`Ž``]_en‰z‚€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~v~u}t|tzu{t|sr€qr{qyqytzu}ww~w|x{y|y}x}v~u€rk~nytxswrwtwwwwvwuuuuwvwwxvxvxvyvxwyxwxxzw{wywwytysysxuxvxwxwyvwtvsuttuuvvwwwywxwwvwwwwxvwvwvvxvxvxuwvvuutwswtytyuyuyuywvwuwtwvuxtxtyuzv|w|w{u{t|u|uzvyuyuyuxvvvvuxszt|s|t{r{szr{s|t{t{t{t{s|s}ss~t|r{r{r|s}r|r{r{t|t~q{{}~~z€t„p‡n‡m„l‚i…f†d‰b‹a‹_Š^‰]‹\\[^‹_‹^]Ž]]_‘`’baŽc‹a‰aŠ`‹aŒ_^e‹q…|€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€zn{n}k{j{i|i}j~kk}myovptpun|l}j|lvpsnulyj{k|k~jk~n{pxowqwswuwwwwwwxwywzxxwxvxvxuvvwvvwvxvxwwwwxvxtxtxtwtwuwvwtwqwpwqwrxsyszuzwxxvwvvvvwvvvvwvwvxwxwyvzuytytztzs{s{s{szuyvwuuvtuuuuuvtvtvuwuyuzuzuzuzvzuzuztztxuyuyuzt{t{s{r|s}q~st}u{vzu{u}u~ut}s|r|q|s}s}s|rxswuyuzsx|}~}z|uq‚n‡l‰l‰j‰f‡c†`‰_‹^Š^‰\‰]‹\[Ž[[ŒZZŽZZ‘\‘_‘aa`Ž_Œ_Š]Š]Š]‹^‹e‡t~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€ysyuzu|s}r}r|uzvyvxwwvwuwtxtztysyryrxqyqyqxrysytzsysxrwrwtwuwuwvxvzwzwzxxxwwwvwvvuuututwvwvwvwwwvwvwvwvvvuvuuuutusvrwrwswsxtyuyxwztxsvtvuxuzu{t{t{u{v{vyuztztzuyuwtxryrzsytxtxtwtvuuutttsusutvuvuvtvuvuwuxuxtyszs|t~u}t}s}s|s|s|s|t|t{t{uyu{v}u~us|r{r|rr€qqr|r{t{rxuw|}~~z|u~qo†mŒljŽfŠb†_‡]Š]‹^‹]‹_Œ_Ž][[ZŽ[Ž[Ž[Ž]__`_baŒ]Œ]Œ]Œ_‹h‡y€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€uuwvwtyszrzszvxwxwwvwuxtysysyswswsxsxtxuwwwwvvvuwsyrxrxsxsxrxtwvwwwwxwxwxvxvxuxtvtvuvvvwwwwvwvwvwvwuwuvwvxvyvwwuwtxswsvtvtwwxwyxxxwwuysysytyuyt{t{u{wywwwwvyvzuxuwuvvwvwvwuwtzs{sztwvtwtutsusvsvtvtvuuuuwvwwvytztzt|u}u}s|s{s{s{t{t{szsxsvrwr{r|s|t{tztzs{s|r}q~q~r~t{lwsy}~~~y~u€q‚nƒn†lŠiŠfŠc‰`‰^Š^Š^Š_‹_Œ`^[[[\]``ŒaŠbŠc‹bŒa_Œ^‹^Œ]bŒm‰|€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wrxswrvrwqwpyqytyvwtwrxqzqyqyqyrxswtvuvvwuyrzqzryqyqypzpyqyrytyvxwwwxuxryqzrzrzsxsxtwtvuvuvvwuxuyuxtwtwtwuwwxvytxuxvwvvuvtwvxvxuxtwvwyvywwwvwvuwuxvxvxvyvyvxvwvvvxwxwxwwwvwwvzvztzuxtwtysyryrxswtvuvvuvuwvwwvwuyuyvzvzvzu{tztzuzuztzs{szsyryszs{t{uzuyuytztzt{s{q}q~t|svyx~~~}z}w~r~nl‚i†h‡fˆdˆa†_†_‡^‰_Š^‹_Œ_[Y[Š]‰^‰`Šb‹c‹cbŽ`Ž^ŽZ[\Œ_‹c‹p†}€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€vswvxwxwxsyryrxuwwvvvrvpxnwmxnxowrvtuwtvvuxsyryswsxtwsyryrytyvxxwxvxvtvrwpxqxswuvwvwuvuvuwtxuxvwwwwwvwvvvtwswtwvwyxzwxwvuuvvvwvvvvuxvyvzvyuxuwvvvvvvvxuyuxvwvwvxvzuyuwvvwuwwwzw{uytysyrzrzrxrxsxtwuwvvvwvwvxuyuzt{t{s{s{r|s~r~s}s{szs}r~s~s~s|s{szs{s|r~r~s|u{t{s|r~sq}z{~}}z|v~som„j†g†g…d„a„^†]‡]‰^Œ^^][ŽYŽY[‹]Š_‹bŠeŒdbŽ\[ŽZŽZŽZŽ]‹bŠp†}€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€utwuwuxuxtxswsvutxtzuxwtyqxpwpwqwrwsvtuuvvwwvuwuwvwvvvwuwuwuwuwuwuvwuwuvvswqwrwuuwuwwwxxyxxxwwuwuwuywzxywxwwvxvywywxwvwuuuvvuxvxvwvxvxuytzsytxuwvwvzu|u|v|vzvyvwvwuwtwuvvuvvvxvxvxwxuwtwuvvvvvuxtzt{tztysxsyszs{t{t{s|r{q{q{q|r|s{t{t|s|s|t|s{r{q{q{r|s|t{tzuytzs|s}t~q{y|~}~x~ur€pm‚kƒj„h†eˆb‹^‹[Œ[Œ]Ž__`Ž_Œ]Ž[Z\Ž_a‹dŒdda]Œ\‹[ŒYŒ^Šd†r‚~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€vtwtwsusvsvsvuuwuywyxxyvytxrwrwswswrxqxpxqyrwrxrwtvuvvvuuuusvrxpyoysyuwwwvwswtwuwwwwzw{wzwxwwwvwvwvwwxyxxywxwwwvwvwvwuvutuuwuxwxwwwvwvvvvvuvwvvwvxu{u|w|v|vzvxvwwwuxsxsvrusttuuvvwwwvwvwvwvwwxwyvzv{u{tzsysxsxs{u{u|s{syrysyrzs{t|u|u|s{u{u{t{t|r}r|rztxuvtxtzs{t|u{s{qz{}}}x}ur‚o‚ljƒh†f‹dŽb`]‹\Š^‹`Œ`ŒbŒa^]Z]_]`_`ŽaŒ^‹]‹YY\Œa‰r…~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€stwsxrxrxrwrwsvtwvyvzuzsxrwrwswuwuwtysyqyqxrwsvswtwuvtutvtusvqwpwoyqyswuwuwuwuwuvuwtxuxvvuvuwuwuvvvtwtxuxwwwwvwuwvvxuxrxswuwwwxwwwvvvvwvvwuvuvuvuwuxvwvwvwuwtwtyuztztyuvttususvtuvvwvvwvwvwvwywzvyuyuwvwuwswswsyt{t|s|r{rzsztzuzvzv{v{s|t{t{t|r{q{qzsxuvvvuxr{r|r|t{qyry{}|{wzs|o€l‚jƒg…gˆe‹cŒbŒ`‹]‹\‹^Œ`Ž_Ž_^Œ_‹^‹Z[Ž^^___]\Ž[YXŒ[Œ`Šq‡~‚€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€ufzh|i{nzpxqwrwswszqzqzqxswuwvwwwwwwxvxuysxswsvuvvvvwtwsyryrxrxqwrxsxswuvtvvvvvvuvwvvvvuvtvswswtwvwvwvwvwwwwwwvvvwvxuytytyuxwvxvwuvtvuvuvvuvuututvuwvxwxwwwvvwvyu{v{vywwwuwtvswsvsvuvvwuwuwvwyv{szsxsvsusurwryszs{tzt{s{szsztzuyuytzt{r~r~r}s}rzryrxsvtvtwtyq|q|r{uxouqv{~{{sxnzjhƒh‡f‹d‹b‹bŠaŠ_Š^Œ^_^’\’]\Ž^_Œ_\Ž]Ž`Ž`ŽbŒcŒ`Œ`‹^‹[ŒY\ŒaŠq†€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€skwgyf|m{qxtwuvwwuxryqxrwtwvxvxwwwwwvvwtzs{rzrxuuwuyvwwvytxtvtvtwuwuwuwwvvvwvwvwwxvwvyvyvywywxvxvxvwwwxwyxxywyuwuwvvwvwwvxvxuxuxuwuvvwwwywwwuuststttvvvwvyuytzuxvxwxwwvxvxvvvswqwqvuvxvxvxwxu{t|szrxswuvuvtytzs|r{sysyszrzrzq{r{r{rzs{q}q}r}s}r|r{r{r|r|r|r|s}s}uzwtoprv|~~xp€lh‚g„eˆc‹cŒb‹b‰a‰`‹_^ŽaŽ_]]\`Ž^\Ž[ZŽ\]]]Ž^`Ž_^Ž\Ž]ŽaŒr‡q‹f„cxqqxtwuwvvvwtxuxwwxvxuxuxwxwxwvvtvswtyszsyuxxvyvywxxvxvvvvvwwxwwvwwxvxvwuwuvwvwvwvxwyw{w{vzuyuxwxxwyxxywxvwvvwvvxwywzwyvxuytytwuuwtxuxvvvutuuuuvvswsyr{szvxuvvwuyuzvxwwwvvvvvwxwxvyvxwyvzwzvyuwwwwwxvuwtxrzr{q|r|r{r{s{s{t{uzszs{r}q}r}q}p|p|q|q~qp~p}r|p~p€q}lztz}~{t„m‡h‡g‡fˆdŠb‰aˆa‡`‰`‰^Œ]\^^Œ__Œ^Ž`]ŽZŽYŽY[\\\Ž\\_]\\`‹r†€e„Aƒk{hptsxrxqxqwsxvwwwwwvwwxwwwwvxtxuwuvuvuvuvvwvwuwvwwvxwxwwwwxvwvwvvwwwxvyvxuwvxuxtxtxtwxwxxwwxvxtysytwvxvwvvvvvvvwwyyyyxwwvxtytxsuttuuvtwtxswtwvuyszrzs{szsxswrxrxsyuyvyv{v{uztyuwuwuwuxuxuwuvtwvuvuvtvuvwtxrzq{r{rysxsxtxtzu{t{s|q|q~q~q}q}q|q|p|p}p}p}q}p}q~o€l|vz}}xo„i†g…f†d‡b‡`†^†]†]‰\‹]\ZŒZ‹[‰ZŠ\Š[‹ZŒ[Œ[\[[\‹\‰\‰[‹YŽ\[[ZŒ_‰p„~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€hƒQ|gwinqrxtyryqxtxvxvxuztztzuyuxuwtxuwuxvwvwwvvwtxsxtwwvywxwxwwxvxwwxwxxwyuyvyvxvytzt{tyuxvvwwxwzw{uzsytwuwvuxsxsxtvwwxxyxxxwvwuwvxtwtwuwvwvwwuwvwwuztzrzszszrzrysxsytytzuzuzuztzsztxtwsxsysysxswsxtyuytxtyuzvzvzuxtxtxsys{s{q|s{r{s{s{r|r}r{s|s|q}p|p{p{p}q}s|u{ryrwz{z|q}kg„e…c„a…`†_†_†_‡`‹__^Ž]Œ]‹Z‹ZŒ]ŒZŒYŒY[Žaab`‹^‰_‰]Š\Œ][\ŽZŽ_Œo‰~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€M†bƒInjhnmxtwtwtvuvvwuxvyuztysyrxryqyqysxswtwuwtwrxrxswtwwwwwwwwwvwvwwwvxtyuxwwwvvxtxtxtxtwuvuvvwxxyxxwwuuuvuvwtxtxuwxuzvzvywvxuwtwutvtwuwvyvyvxvwvwuxuyswsxsyrzs{t{u{u|t|t{uzuyvzv{vztyszs|s|t{tytyuzuztzt|t|u|tzuxvwuwtzr{q|p|q{p|q}q~q~r}r|s|s|s}r}q}p~p€q€r~uyrsuv{|v|kf„e‡dˆbˆ`‡`ˆ`ˆ`‡`ˆ`‹^Œ^Ž]Ž]Ž[[Ž[Ž]Ž]Ž[Ž[Ž[\Ž\Ž^_Œ_‹_‹`‹]]Ž[Ž\]a‹pˆ~‚€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€a„uYqmqtjyqxswsvsxsyszuzvyvxuxsxqypypxrwtwtxsxsxsxswuxtwuwuvvuxvxvwvvwtxrxsxvvwvvvtvtuuuvuvuwuwwwwwwvwvwvwvvvwuvvvwvxtxvwuwwuwtwsvttwuyuzv{v{vxvxvxtytytxtwtxtzu{t{uzt{szsytxtyuzv{uztzt{t{u{uztytyuyuyvzwzvzvytytxuxuytyrzrzrzr{q{p|q|q|r|r{r|s}s}t}s~rp€qu}uxstxy{}qgfe„c‡a‰aˆcˆ`‰_‰[ˆ[‰[ŠZ‹ZŒ[ZŽ\Œ\‹_‹`Œa]ŽZŽXŽXX[Ž^Œ`‹bŒ`]\Z‹^‹dŠq‡~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~s‚|€[jjapcxpwrvrvsysztyvxyvyvxvtvrwqvrwswswtwtwuwvwwwwxtxswtuvuwvvvwvvvvwvxvwvwvwuvuuttttuuvuwuwuxuxvyvywyxwxwwvwvwwuwtytytxvvwuwtwvtyu{u|v{wyvwvwuwtyuzuytytxtzu{t{u{uxtwtvsvtxtyt{sztztztzt{uyuxtxsys{sztyvwwvuwtys{s|t{t{uzt{t{t{rzryqyrytzt|t}t}t}sqoq€t|swuy{~w‚l…f…d‚d‚c„`…a‡aˆ`‰`‰^‰^‰^Š^Œ]Ž]][Ž\Œ]Œ^_ZZXXXZ[ZŽ\^^Ž\[ŒZŒ`Œo‰~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€r‚{€n€mprnvsututvsxtxuxwwxwwvvvuwtwtvuvuwtwuxuxvxxxyxxxvxuvvuwvwwvwwwxwyxzwzwyvyuwuwuvvvvvwwwvwvvxu{v|u}v|wzwyvxwyvzvztztztzuzuyvztzsytztxuxuxvyvyvxvyuzu{tztztzuzvzuzuytytyuxuxuxuztzuytzr|q}rzsytytzs{t{s{sytyszr{q}r|s|t{t{s}sqq~q~p}r}s|s}s~r~r~qp€oqrmzsxy{r~h„d…b…`†`‡^‡^‰^‰^‰aŠa‹cŒceŽb_‘]‘X‘ZZ\]Z‘W‘VWYZ\[X‘Z‘\ZZZŒ`Šq†€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€@~T{x~lwtwtwtuvtwtwuvwvywywwyuytyuwvwwwwwwuvuvvwwxyxyxwxwyuzvzvyvwvwvxwxwyv{t{s|s{tyuvvuwvwwwwwwwyw|w}w}w{wzvzuxtxsxuwuxv{u|u~u}u|s{rysytxtztzv{w|v{u{t{szsztyuyuyvztzt{tzuzvxuwuwvxvxvvvwsyrzrzqyqzp|p}q~q~q}r}r}r}q~r}t{txuyt{t}r}r}r~qrr~q~pp€qqqqr‚kƒgt|uj‚b‡`‰`ˆ^†^†]‡]ˆ]ˆ]‰]Š_aŽcdŽcŽ_Ž\[Y‘Y‘]^‘]‘[‘YXZ]`Žba_\‘YWX`sˆ€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€<sIrv{nvuwuxuwuvvwvwvwvwwwwwyvxvwvvwwwwwwwvvuuvuwvwvxvxuxwwxwxwxvxuvuuuvvvuxtzt{t{uytxtyuyvyvxvxv{u|u}v|wzwyvyuxtwtvuwuxvzuzvzvzwzvztztztxtxuxvyvzuzs|s|s{s{uzuytytzt|u|v{v{uzuytxuwuwwvxuvwuyt|s|p|n{o{p|r~q~s}t{s{s}r}s{sxtwtyr{s|s|s|r}q}q}q~qq€r€s€s€s€tƒh„l~x~tƒj‡eˆc‡a‡`†_†^ˆ^‹^‹^Š^‰]Œ[^_Œbaa`___]\‘ZZ[Ž_ŽabŒbŒbŒ_Ž\Z‘X’Y‘b‘t‹‚€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€l‚k{z|omvsvvvuwtwtxuwvwuvtwvwwvxwwvwwwwwwwwvwuwvwvwvwvxuyuyvxxwwvwvvuvuvvvvvvwvxvyvyuxuyuyvywxwwvytzt{uywxvxvxtxuxtxtytwuwtvvwvxuyuzu{u{uyuwwuwuwwvyu{t}t}t|tztxswsxuzvzw{u|t{t{tyuxuyvyuyu{uzu{u{t{s{szsytytytytys{s}s}s{rzszr|q~p~q}p}q}o}n|p|q|q}q~r~strƒlqyw{r€j‡gŠe‰cˆb‡aˆa‰`Š_‰_‰`‰^Š[Œ\Œ^‹bŒaŒ``Ž`_^Ž[ŽZŽXŽ[^``_ŒaŒ_Œ]Ž\Ž\Z‘]‘d’tŒ‚€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€p‚jzz{qgwowswrwsvtvvwvxuxtxuwvwwwwwwwxwxvwvuwtxvxvwwvxwxwxxxxxxxwxwwuxuwuvvvvwvwwwxwwwwxvyvzwywxwwuxtxtyuwvwvxuyuztysytxtwtvtwuytytyuzw{xzwxuvuvvuwvwxuytzszszryrxswtwuyt{t}r|t{u{u{t|s{r|r{tzuyuzu|s}s|sztxuxu{u|ttt}s|q|r{q}q}q~q~q~p€oo}pzqyp{p|p~su~pm|v{v|mh†fŠe‹b‰b‰a‰aŠ_‰^‰^‰_Š`Š_Œ^]^^\]^ZYŽXŽYŽ[^`Œ``_Ž_Ž]ŽZZŽZZ^eu‰€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€z€bowvtczkxqwrvsutuvvvwuxuxtwvwvwvwvwwwwwvwuxtzuzuxvvwvxwywywyxxwywxvxvxvvvvvuwuxuyvywwwvxuwuwuwvvwvwwwwwwxwwvwyvzuzuytwsxtyuzuzuyvxvxwytztzryswtuuuvwtytytzsyszszszryryrzr{r{t|uzuztztztzuyuxsyq{r}r~s|s{tytzt}utt}t}s}r~r}p|p}pq€q€q€qq}s{rzqzqzr{s}r|lzpzy|u|l~hf…dˆc‰aˆ`‡_‡_‡^ˆ^ˆa‰b‰`Š]‹]‹_‹bb_‘^‘[[Ž[]^ŽaŒ_‹_‹aŠa‹^Œ[Œ[ZXŽYŽ]Žfx†€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€‚YŠBƒnot`}kzpxqwrwrwtwtxtxtwtwwvxwxwvwtxuxuwuyvzvzuyuxuwwwzwzwzwzuxuvvuwuvuvvvvwvxwywyxxwwvuuuuuvuvtwuwvxvxvwuwtwtxsysxtxuxuxwxwwxuxuxwxyuztzsyryszszt|s|tzuzuyuytysyrypypyrzrzs{szrzqyqzrztztzt|s~r~s}s{t{t|t|t{v{u{tzs{s|r}s~q}q}q}r~r}r|r|r|s|s{s{r{s{s}o}i{rzz}u}mg€e‚c†b‡`‡^‡^‡^‰_Š`‹c‹cŠ`‹_‹`‹a‹ba_`]]]Ž]]_aaŒ`Œ`Œ\ŒY‹\Œ[Œ[Œ\^Œiz…€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€c‹n‹pyzh€d~d}f{k{kzn{pzryswsvuuuwuxtxswswtwtyvxwxwwwwvwwwywxwxuxuwuwwwxwwxvyvxwwywyxywxwwwvwwvwwvwuxuxuwvvvuvtttuvtxsysztzuxvwwvwuwwwxwzwyuytxsxszszu{uztxswswsxsyuysxswrwswsxrzr{q|r|s|s{tzuzu}u€t~r}r{t{t{v{u{u{t{szr{r|s{t{t{s{r|q|q}s}s}r}p~n~p}p~qq}nzmwvyy~s€lƒf‚e„c‡bˆ`‰_ˆ^‰bŠcŒcbŒaŒ_Œ]Œ^Œ^Œ_```_Ž^Ž^\^^Ž_Ž^]Ž\[YŽYŽ]ZŒ_ŒcŠn‰|ƒ€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€w‚z||~z~u}p|l|h~d€cƒc‚e€lzqxswsxsysxsxsxrxsyuwwwvwuwuxvxxwxvwuwuvuvwxwyvzvzvxwuxsxqyrxsxuvwvxwxwxwxvxvwwvxuxtvtuusvrwtwuwvuvwwxwyvxwxwxwwvwuwtwtwtxuyvyuxtxsxsxsxtxsxsxsxsxsxtxtytyu{v{u{uztzt|t}s|r|r{r{r{sztztzt{s|r{rzsxtwtwtyryqzp|q|s|r|p~n}n}n}oq|nsrqyyy}pkg~dc„c†a‡a…_‡bˆd‰eŠbŠ_Œ\[]]Œ]Œ`Ža_Ž]]^]]Œ^Œ]Œ_Œ\[Ž\Ž[ŽZ\Ž]Ž_g‹t†~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~}{~vqo}jzlwtvvwvvvvwuwvwxuytzuyuxuyuxvxxwzuzuztxuwtvtvswrysysxtvtutuuuuuvvvvvvwvxuwwwywywxwxwvvvtususvtvvvvuvxvzwywxwwwvvvtvswsxtxtxvwxtwvuwtysysytzt{tzuytyuxuxuxuxvzuzuyvyuxuyt{r{q|q|q{r{rytxtwuytyryrxrwsxtytzr|q|p~oqq~r}q{p{o|np|ouss{{z}p~ieƒb‡`Š_Š^ˆ^…`†b‡c‰eŠa‹__Ž`Ž`Ž_Œ^_Ž_Ž_Ž][ZŽ[^Œ^‹]‹`Œ_‹_Œ[ŽYZ[^`gv‡~~~~~~~}{}y}|{ysttxuzuzuztzuyvywwxwxwxwxvwvvvvwvxvxvwuxuxtxtxsxrxswtxuxuwuwtwuutuututvuwuvuuwuxuxvxvwuvvvtxu{t|u{wxwywzwzvzuytyryrxrxryuzvztztvswsxsysysxsxtyvxvyvyuyuzuzuztzs{s{tzuyuzt{s{q{q{q{r{rztytytyqzqzq{r{r{s}s}s}r|p}qq€qqp}o|o}qp}oxtx|}x~oie„bˆ`Š^Š]‰\Š_^Ž`a‹aŠbŒbcŒc‹`‹_`Ž`^‘]‘[‘Y‘Z\\Œ]‹\Œ\Œ\YŽUY[\^dv‡~~~~~~~~€€€€€€€€€€€€€€~~|~|~~}{vtwvwxvwvvvvwvxuyuyvwxwxwxvvvuvswtxtwuvwuyuzt{sztyuxvwwwxuwuvuvuuvuvuvvwwvuvuvuvvvvvuwtxuyu{u}t}u{wwxuxwwyv{vzvyuxtwrwsvuxuzr|r{qzrxqyrysxsxsxtxtxtyuzuzu{t{s{s{s{s|s|s|s{szrzr{s|s|t|t|s|s|s{r{q|r{r|sztysyqzo{p}p~q€p€o~o|p|s}owqrww||u|mh€fec`…`‡_ŠaŒ`_Œ_Œa‹cŒeŠe‰c‰_‹__^^’\’]‘]]\Ž[^‹]‹^Œ\ZŽXŽ[\`bŽgŽy†€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~~~~~~~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~~|yqzsyuxtxsxuyvzuztzrxswuxvwvwuwsvtwtwwvxuytytyuxuxuwuwuuvtxsxsxtwuvvvtvtvtvuvvwwwwvvvvuxuzu|u|tyuwvtxswuwyv{vzwwvwvwuxtxswswsxsxtwvwuxuyuyuyuzu{u{uzuzszs{r{s|t{t{t{szszrzszsysytytzs{s{s|r|s|r|q|p{q{sytyt{p}n~n~o}p~q€q€q€s|tzotqqyy{|s|lh‚f‚dc`‚_†`‰b‹cŒcŒcŒd‹e‹d‹cŠbŠ`‹c‹d‹bb]‘\\\]]^Œ_‹^‹]Œ^Œ_^abŽeŒiŒz„€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~~~~~~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~}}}~~}zzl{myswvxuxuyuztzszrysxuxvxwxvxuxtytyuxvwvvwvxuyuyuytxtvutwswrvsvuuuuuutvtxuyvyvyvyvywxxxzv|t{syuvvtxtwwxyvzvyuwuvvxsyszqzrxswtwuwuxuyuzuyuyvyu{t|tzsxswswrzr|t}s}s{rzszs{s|s{szszrzq{q|q|q|r|r|r}q~q}r|s|s}p~mkmprsƒs‚u{uwssuv{{z}qkhƒf†d„aƒ`„_…_‡aŠdŒdŽdŽdŒa‹b‹bŒ`ŒbŒd‹e‰dŠcŠb`_]^Ž`Ž_][ŽY]^Ž\^_e‹i‰{‚€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€}|{wyw{wyn{izpwuwvxuyuztztzsytxvxxxyxzyzyyxvxuxtwuvvuxuxvxvxtxuwuvvvvvtutvswtwuwuwxxxxxxxwwwwwwxxxxzvzuzuxuvvwuxuyuzuxuvtutwsyszuzuytyszr{r|s|s|tytwuxuzt|s{rxrwrxrzs{s{rzsyrys{r}s~s}s}r}p}p|p{rzryrzs{s{r{r{r|s{rzqzo{n}o~oqrs~t|quqpwt||z}t~n€iƒe‡d‡b†a„_…`†aˆb‹cŒdŒdŠdŠd‰dŠd‹b‹a‹`Ša‰bŠa‹aŽa_]\\\‘Z’X[Ž\\Ž\‘\cŽhz‡€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~}~~~~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~|{uuqtvuyuwlxqyvxwyvytztyszrzrzsxwxzx{x{xywwwvwvxwvwvwvwwvxwvxvxvxuxuxuvuvvuvwvxvxwxwxxxxywyvxwvxuxvwxvwwwvvvvuwsxtxtwtvtwtxtzuzvxuxuxtytztzu{uzuyuxtyt{u{tztysxtxtytzs{sysysyr{s|s|r{szrzrzrzsytxuwuxuztzszszsztytytysyszs{s{s|s|s}tzsnsixr}|z|s~m‚g†d‡b†a„`‚`„b…bˆ`‰bŠc‰fˆhˆg‡eˆcŠ`‹aŒ_Œ`ŒaŒ_Œ^Ž^[ZZZ]\\\Ž^Œ`_\‘`e“x‰~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~~~}~~~~~~~~~~~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~{|r{pysvzyxrwqytyszryryrwqwpwpwswvwxvxwxvxvxvyvyxwxwxvwvwvvxvxwwwvvvtxsztzuxvwwvvuvuuvwvvwwwwvwvxvwvwvxxxxvwuwsvrwswswtwuxuyuytxtxuwuwvvvwuyuyuytysyrzq|s}t{s{rysys{t{s{s{s{r|r~r~s}s|szsxswsyr{t{tytxsyr{r|r}r}t|s|r|q|q|s|s|s|r{s{uvpqrpyx~}z}rk‚g„c…`ƒ_ƒ_„`…b‡b‡bˆb‰aŠb‹e‹d‹d‹`_`ŽaŽaa‹_‹_‹]ŽZ[\]`ŽcŽdŽ`Ž^^Ž\Ž\`d‘xˆ~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~~~}~~~~~~~~~~~~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€y|s{qzsx{{{wzlznxpvsuututsvrwswuvwuxuxtxuvuvuytztzuxxvytwtvuvvwvxuxvuxtzt{uzvxvwuvuuuvuvuvvvwvwwwuwuwuwwuxuxtxtwsxsxtzvxvwwuustusvuwwvwuvvtvuwtwsxryr{r|s}t|szrxrxryszr{szrzr{r{r}s}s|tztysyszr{s|r{qzqzqyq{q}opp~q~q}q~q~p|o}o}r|qwmspuz|z~pie‚ba‚_…_‡^‰_‹`Ša‹b‹aŒ`ŒcŒbŽaŽ____Ž_^^`Œ^Œ]]Œ\Œ\Œ[^aŽaŽ^[Ž[]`du‰€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~~~~~~~~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~v|q|qyqu{{~{{l|b{cxjuptttuuuwuwvvwvyuyuwtvvuvwuxuxvwxvxuwvwvwuxtyrxtvvuxuyvwxvxvwwvxvyvytzsyuwvuwtwuxvwwtysxsxswtvtwuxvwvuwrvrvuuwuxvwvvvuuvtwtxszs|s}s}s|s{sxsxsxryr{r{rzsytzr|r}r|szuxuxsysysyr{p{ozpyqxqzq|o~op~q}s|s|r|p|n}no~jymstt||zrkfƒdƒbƒ`…`‡^ˆ_‰a‰bŠb‹bŠ_Š`Š_Š_Œ_Œ`_][\^bŽabŒb‹`Œ^YŽ\___[Œ]Œ^aŽes‹‚€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~~~~~~~~~~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€}}uytyswsu||~}|xyqwmwiufqlovtxwvwvvwvyvyuytyuxvwxwxuxuxuwuxvxvwwwuwuwvvvuwuwuvwvvwvxuxuxvyu{sztxuuwtwwwywyvxtvsurususxsytxswtwuxu{u{t{tzvwvvuwuxtys{s{s{r|r}r}s{s{szrzrztytxuxuzs}r~q~r{tztysysysxtyr{q|p|p|q|q}q}q~qr~s}t}r}r|q|s~s|lzlwuy~~zqj…e†b†a†`†`‡`ˆ_‡`ˆ`Š`Œ`Œ^Œ^Œ^Š^Š^‹_^Ž][[\‘]‘\‘^`Ž^]]]Ž[ŽXŽ[WŒ[Z_Žfuˆ€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~}~}~}~}~~~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€{|qzs{rxtv||}|{yywxvuqmmkurxwxxwwxwwwvvvuvuvvwwvxvxuwuwuxtytxvxwxwxwwwvwtvtwuwtytyuwuvvwvxuxvwwvxwwxvxvvwvvuuvtvsxsytytyszr{s|s~t}u|v{vyuxtxtxtxtztzszr{r{r|r|r{s{szrytwuwtxtyt{t|s|rztytzs{r{qyszs|r~q€qqq}o|o}p~r~r~s~r~q~q|t}uysrrqxy~~ypjg‚d„b†`ˆ_‰_Š_Š_Š^Š^‹^Œ^^^`Œ`Œ`Œ__^‘_“_“`’^‘^`__][[X\XŒ[^`Žh‹w†€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~}~}~}~}~}~~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~v}jon{sw}}~}|{{zzxwtpnisqxwxxwxwxtxsxsxqwrwsvtwuwuwvwwxvxuxvxvxwxwwwuwuuuvvwvwuxuxvyvyvxuvuvtvuxuxvxvxvxtysysxuwuvvuuwuytzs|r}r}t|vzvyvxtytxtwsyrysytzszszrzsytzs{r{rysxrxsytytytzsztytyrzq|q|r|s|s}rq€pp~o|n|o|q|q}q}q}q~r}qo|rtwq{zz|rynzj~gd„a†a‡bˆ`‰_‰^‰]‰]Š]‹^Œ]^_``aŽ`‘a‘a‘eba_]]Z[‘Z‘\^Ž]Œ]Œ`ai‹u‡~‚€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~~~}~}}}~}~}~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~{yswn}o€o}tz|}~||z|z{yyvqnmstxwxwwwvvuwtxvxvxuwswrvtwuxwywxwwvvxuxuywyxxxzxywwvwvuvvuvuwuxuwtvtwsutuuvwxwyuytyryswvvwvvwuxuyuzu{s|r}r{s{tztzu{t{tzrzrzszs|r|r}s}uzvytysyrzsyryryryryrzr|r|q{qzr{r{s{szrzq}ppqr}r|q{p{pzp{p{q|r|szkym{t}|~z|pyj{g€dƒa…_†_†a‡_‰_ˆ]ˆ]ˆ]‰^‹]]Ž[Ž\Ž^Ž_Ža__‘^’a‘a‘_’]‘]\[^]]``Œ_`af‹n‰}‚€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~~}}|}||{}|~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€|zwrvuv{s}n}rz|}~|{zyzz{yyvqnnruvvwvwuwuwvwvxvyvxvwvvvwwywzxzwxwwuytztzuzv{v}v|uzuxuwswswtvuwuwtxtytytyuyuyvytxsvrvsvtvuvvwvvvuvwuytztzsxswszt{u|t}t}s|t{uzt{s}q~r~s~s|s{r{r{rzszr|qzp{q{p|p~p~r}r{rzqyqyqyq{p}qr}tztysxryqyq{p|p}q~q}k|k{s||~€€z}q~ie…e‡bˆ`‰_‰_Š^‹^‹^Œ]‹[Œ\ZŽZZŽ^`ba`Ž^__`‘_‘]^Ž__Ž_Ž^[Œ]Œ_^]_ŽcŒgŒy…€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~}|}|}||{}|~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~uzoyrzt{rznurt||~{{xwzy{zyvqnntszu{vzwyvxvxvxuxuyv{v{w{v|wzwywwwwuys{r|r|t|u{u{vzuyuyuytzuxvvvvuwuwuxuxuytxtwswswtwvwvxuyuxuwvvvwuyvyvxuwswsysytyu{u|u}t}u|t{szszs|s}r~p~q}r|qzrxsytxsyr{p|p~p~q~q|q|q|q{rytxszr|p~q|rzrzszqzq|o}p|p|p~m|pwwx}~€€|~t}m~jg…e‡dˆa‰_Š^Œ^Œ^Œ]‹[Œ\Œ[Œ]Œ`ŒcŒd‹d‹b‹bŒ`a^^^\^Ž`ŽaŽ^Ž[XX[Ž\\‘\bŽfvŠ~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~}}}}|}|||}|~~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~‚~}}swoyr{r|p|muts|{{|xyyy{yyvqqkzq~u|w{v{vzvxvxvxvyvzv{v{u{vzwywyvyuys{r{s{tzvyvyvyvxvvwvvxvxuzvyvyvwvwvwuwtxuxuwuwvwvwwxvyvxuxuxvyvyuztytwsvtuuvtxtyu{v{v|t{tysytyt{s|r}r|s{tztytxryrzr|r}q~q}q|r{q{r{s|s}s|r{q}q}p~q~r}r|s{q{r|q}q{ryqynwsuyx~~~w}q}mg…eˆdŠb‰a‰_‹^Œ]Œ[ŽYZŒ\Œ^ŒaŽcŽdŽdŽdcŽaa``_][]`Ž_Ž^ŽYŽXŽ\Ž][Z’`‘guŒ}ƒ~}}|}|}{|{}{}}€ƒ}ˆy}{rwmyp|o}m~kysw}}~||xzzy{zzxqtk{s}v{w|w|v{vzvzvzuztztzt{t|u|v{xyvxvwuwuwuwuvuvuxtztztxvwvwwxw{w{w{u{tyuxvyvzw{wzwywvvwwvwuwvwwuxuytysyrzsztyuwtwtwtxtytxsxsytytzt{s}s|t|tzuzuzs{r{q|p~ppqr}r|r|p{p{q{p|q}p}p}q}r}q}q}p{rzryr{q}q}rzpxnwrwy{zso„kŠheb‹aŠ_‹^Œ]ZYZŽ\]^ac‘db`^’^“_’^‘\‘[YYZ[^]Ž[[ZY‘Z^gnŽ|…~}}|}{}{}{}|}}€~ƒx€€€€€€}|wvqws{r}n|mutv}}~z{wxyx{yyxtrowuzv{w|w|x{yxwxwyt{s{sztzu{uzvywwxvxwvvvvvvwvwvvvtxtztzuzuyvzw|w|w|v{uztzt{vzwzxxwwwwwwvvvuvvuxtyszsysyr{s|t|szsytxtwuwswrxqys|t}t}s|rzrztzuzt{r{q|p}p}oqrr~t|s|r{p{p|o|p{q{r{r}q}q~p}o|q{qzr{r|r}rzoyozr|y~€€€€{v‚o‡j‹fbŽabŒaŒ`Œ_Ž]Ž\Ž\Ž]^‘_’a‘acbaba‘`‘^’\’Z‘ZŽXXŽY\`]Z‘Z[Y\cŽhx†€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~}~|}{}{}|~}~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€{€€€€€€€€~}xzsxtuwnxqvyz~~~~y{wyyx{zzxssoxs{s}t}u|xzxwxvwuvwuxuxuxtxtxuwvwwvwvvvvvvwvyvyvxvxvxvxtytyuyuzvyuzuzv{u{u{uzvyvwvxvxxyvzu{s{q|s|r{tzuytyszs{r{r|r|t{vztzs{r{r|s|s|r|r{rzszt{s{r|q}q~q|q|r{q|r|r{r{qzq{q{p|q}r|p|q}ppq€p€pp}q|r|s{ryoxnys{z}€€€€€~xn†gŠd‹a‰bˆcˆc‰bŠa‹_\Ž\_‘_‘a’cccŽdŽeŽecŽa^‘[’X‘VŽTVX[bŽbŽ_^]Z‘Z’`fu‰€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€}~}}}}{}|}}~}€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€}€€€€€€€€€~~z|xzxyx{z~}~}zyyy{z|zzxustuwwvzt|u|vzxyxxxwwwwvvvtvsvsvswtwuwuwuxuzs{s{sztyuxtxuyuzuyvyvyvyuzvzu{tztztyvxvyvzv{w}w}w}v}t}t}t{uyuytytztzszszszt{tztztztzt{r{r|r}s}r}s|r{rzr{r|s}t}t|szsyr{q|r}q{szszr|q}p~oop€ppn€noq~s|vxswmvnutv||€€€€€€€z‚o‡f‰b‰^‡^‡^†_ˆ_‰_‹_`Ž^Ž``a‘b‘bcdŽeŽdŽa`]’Z“W’UUW\‘^‘a‘a^‘^’]’Z‘Y‘Z‘ao}…€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€}~}}}}|}}~}}€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~~~~€~}||{|{{yywsrtvxvvxuxuyuzv{v{x{xyvyuxtusttrusvvvxuytzszsztzuytxuxtxuywywxxxwyvyuyuytyrysyswtwuxtzvzwzwzw{v{u{v{vzwywxvxuytyuxuytyr|r|s{tzt{s}s~s|uzu{u{s{sztxtyt{s|r~r~r|szs{t|s|s{s|s}qo€mmopq~p~pqq~q~q~t{m}g{lvut}|€€€€€€€€€|€r…gˆbŠ_‰^ˆ]ˆ\‰]Š^`ŽaŽ````Ž`bŽdŽeŽfŽc]’[“Y”Y”X’Z‘[‘[^^_]‘Z’[’\‘[XZ‘]‘e•xŠ€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~}}}{}{}{~}~}€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~~}}{yxvqoptvwwvvuuvuxuyvzw{wzvyuyuvutvtwuwxwywywyvxwvwvxwwwwvvuvvwuwuwuvvuxtyuytzs{rzsxsustsutuuvuwuzt|t|u{v{vzvytysytyuxuytzr|s{s{uzt{t{t{uyvxvxuxtwswtxszr{r|r|t|t}t|t|s{s{s{s}t~srq}p}p{p|q}q}q}r}r|q|s|tzl{hzlwuw}}€€€€€€€€€€~uj†f‰b‰a‰`ˆ_‰`‹^`_a`‹_Œ^_Ž`ŽbŽeŽfe`’Y”X”Y“\^aŒa``^‘^’[”Z•Y•Y”Z“_’`‘du‰€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~}}}|}|}|}}}}~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€}{zxxtrkumzsysxtvtuuuvvwwxwywzwyxxxwxwwxwywywxwvwuxtxuxvxwxxxwwuxsxrxsxvwxuxvwtwtxsxtuvsurtuuxuzu{u{u{uyuyuzt{r{szrzszt{tzuzsysxsxtytytytytytytxtvsvswsys|r}s|s{uzu|u|s|r{q{r|r}t}s}s|s|s{rzrzr{r|r|s|s{t{u}xwsqporpxv~}€€€€€€€€€€w}n~iƒe…bˆ`ˆ`Š_Š]‹__bŽabŒaŒ`Žaabdca“\”[“`‘bbcŒccc‘a’`’]“Y“[“[“]‘]dŒi‹r‰}‚€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~}|}}~}~}~}}|~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~}|{yywwtqnqrwvwvuuuuuvuvvvvxvyv{x{yyyxyxwwvxvytytytxtwuwvxvxwwxvvvvutttvtxv{w{vzuxtwtwtvtxuyv{v|w|v|vzsytxtyt{t{r|s{szszt{t{szrzqxpyrytytxtytytzszsyryrzr{r}r}r|s{s{r}r}s}r|q}qq~q}r{s{r|r}q}q{r{r{s{s{s{s{t|vyquotpvwz~~€€€€€€€€€€~x}n}jfƒb‰_Š^Œ]Œ\Œ_Ž_Ž`^abacbbca‘`“\”\’`‘`’a’bbcŽba`\X‘X‘Z\[Ž`edw‡€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~~~~}~}~~~~~}~}~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~|zywxvwtonjwrztytxtxuxtxuxvyvyvzvyvyvyvxuytyvzt{rzsysytytytyuyvwvvuvtyszt{t}v}u|u{uzuyuytxtzt|u}v|vzuxsysztzuzvzu{u{uxuwuwtxsyqzryrysxtwsxrxrzs|s|s{rzr{r{r{rztysyq{q~q~r}s|r|p~po}q{s|r}q}q|qyqyqzq{q}q|q}r~q~m|nxrvzz~~€€€€€€€€€€{sm~hd…a‰_‹^^Ž_][‘[]]^_`bd`\‘Y’Y“]“^“`’a‘bba^‘_‘[’X’W‘WXXZ_an|…€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~~}}}}}~}~~~}~}~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€}|zxxvxuwtpnnvuzuyuyuyuzuzuywyvxvxvxvwvxuyu{t}u|v{tzuytyuytyuzvyvwxwxxxzv|v|u}u}t}t{tzuzuyuxtwtvtxtxuytzs|s}t|tztzu{u{vyvwtwsxrypypysysxswrwqyq{q{rzsytyszszszsztzszqzq{p{q{r{r|r{r|rzr|r}r~r}q|qzrzq{p|o|o|o~oo~m{oxruzz€€€€€€€€€€€€~€xq~l~g‚cˆaŒ`Ž_^‘[ZYŽZY‘\^_aa^ZX’Y“]“_’`cŽccb`‘`‘[“X”W“V“W’V“Z’`‘d’e—t‘}„€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~}}|}|~}~~~}~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€}|ywxvxvwtqnqvvzwxwwwwvyvzvywywywywyvxvwuxuzu|u|vzvxuxvxuxuwuwvwvxwxwzxyx{w{w}v}uztytxuwuvvtutustvsxs{s~su~u|szrzrzsyuwuutwsyrzrzrzr{r|s|r{s|r{rzrzszs{t{tztzt|s~s}s|ryryryr{r}s|t|t{t|s}s}r|r|r}r}q|q{o{n|o}pp}mznwqxz|€€€€€€€€€€€€€y~s}mg†c‹aaa’`“^^\Œ^Ž_bdŽdcŽb_[‘Z’]“`“b“a’a‘a‘b‘ed‘c‘_’\’[’Z’\‘\’^’a’a’gk{…€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~}|||}}~}~~~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€||zxywyxxuqopvtzvxwxwxvyvxvxxxxxxyxxwwwvwwwxvzu{vzxxxvxvxwwwtwvwvxwyvzvyuywyxzv{u{tztxuvvuwtwtvuuvuzs|t}t}v{vztzs{q{rztwvvuxtyrzsysyszr|r~s}t|uzvyuyt{t|s|t{s{s{r}r|s{sysztzs{r|s|t|t|t|s{rzrzq|r|r{rxqwoyozrzsznwmrpquw|}€€€€€€€€€€€€{}t|mƒhŠeŒcŽba‘`‘aaŒ`Œa`aaŽcŒcŒaŽ`‘^“\“`‘bd‘a’^“_‘cdddb‘b‘_‘\\Ž[^Ž__dgŠx„€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~}|||||}|~}~~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€}|zxywywxuponwszuywywwwwwvxxxxxzwzwywyxxyvxxwxvyv{wyxxxwwxvxuwuwvwwxwxuxswuwvxvxuysysxsvuuvuwvvxuyuzuzuyuyuxtyt{t|t|t|u{u{uzuytxuwtxtyr{q|r{s{uzu{tzsytytzuzszqzr{s{r|r|q}q|q|r{sztzs{rzqzqzq{p|q|p{pzpyozpyqzswoookrpwx}~€€€€€€€€€€€€€€~~vn…j‰gŠe‹da^^Ž\Ž]][\\]Ž`Œ_Œbb‘`’b‘cc^‘[“]’bbŽaŽaa‘c‘a’]‘Y‘Y\]‘^‘_‘hŒt‹}ƒ€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~}}|}|}|}|~~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~|{yywywxuoqi{q~t{wyxwxwvvvwvwvzw{w{wywwxvywwxwzv{v{vzvxvvuvuwuwvxvxwwwwvvvvuwuwuxsxsxrvttvuvvvyuyuyuyuxuytzszt|u|u{u|t~s~s|uxvvxuvvuwsxryszs{r|q|q{q{s{t|u|u}r}s}t}s}qp€p~r|s{szszryrzq{p}p~p}p|q{p{o|o|p|r{sxnumsprvu}}€€€€€€€€€€€€€€€€€w€o„j‡h‰gŒe_[ŽYYXZZ\__`ŽaeŒdŽbc‘cc]Z‘[‘^‘_‘^^ac‘b’]“Y”Z’]’^‘\’Z“b‘f”xŠ~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~~~|~|||||~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~}{yywxwxupri{q}uzwyxxxuvvvwuxuzuzv{xzxxyxywxxvyuzu{u{tztztyszsztzuyvxvxvyuzuztytyuyuxuwuvvvvxvyu{uzt{szs|s|t|t|s|tzszs|s|s|tyuvvtusutsxrzqzqyqyryszr|s|s|r}q~p€p€pq}q}p|p|q{q|r|r|rzrzrzr{q|q|q{p{p{p|p}q}t|r}kj~oyww~}€€€€€€€€€€€€€€€€zr‚lˆjŒjŽc^Œ[ŒYZŽYŽ\Ž_ŽaŽcŽcŽbcba`’c’e’b’[‘Y[_‘`‘``a_‘`’\“Y”]“b‘ca^‘a’e”s}ƒ€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~~}}|}|}|}}~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~~~~~€€€€€€€€€€€€}|zxxvxvxuqnoutzwyxxwwwuvvwxvyuyuxuzvzvywvwtvvuwtxtzu{t{u{t{szszsztxuxtxuyuzv{vzvzvyvyuyvxuwuxuyt{t|u}s}r~r}s|s|r|s{s{t{tzszszsztwtvswryrzq{rysyuxuxv{t{r{p{o|n~nmo~q|r|r{q{q{pzoyqxrwswsyszr|r|r|q{r|q|r{s{s}l}k~ozwy~~}~v~p€o„m†e‡aˆ]‰[Š\‹]Œ^‹`ŒbŒbŽaŽ`_Ž`aa‘c’f’^”X“Z‘[^`baa^Ž_\’X•Z”`‘cŽcŽab’g“p’|…~~}}}|}}~}~}~~~~}~|~|}~}{zxxvxvxurmruvzvywyvxvwvxvwwwvvvvvxuyuyvwuvtutvtwuwuytytwsvtvsxtxuwtwtvtvtwvxwxxxwwvwuvuwuwtwtxuzu}u~tsr~q~p|r|s{r{szryryr{r|s|r{rzqzqyqysyuxvyvyw|u|t|t{ryqxpxoypzq|r|s{r{qzpzozpzrysytys{r|s|r|q{r{qztxtxsxmrmppqwu~~y}s|q}j€c…`ˆ^Š\Œ[[Ž]Œ`‹a‹aŒ^Ž\Ž^Ž`ŽaŽaŽbŽe[“Y‘^^ŽadŒdŒc‹a‹__\’V•Y“^‘`‘b‘`’^“b‘e”v‹~~~}}|~}~}~}~~€~€}~}~}~~€€€€€€€€€€~~~~~|{zxxvxvxvroqwu{vzwzvyvyvxuvvvuvuvvwvwvwwxvxtysxsxuwuxtwsvsvsvsxsyswsutusvswtxvwwxvxuytyuytztyuxtzu|s}t|s|rzs{rzsztzsytxsxsyq{p}q}p|p|r{r{rzr{szu{u|v}u~u}u|tyrwrwpxpypzp{r{r{syrzq{q}r}s|szs{s{szryqzq|q|qzqzkzjtmpqqyw~~€€€€€€€€€€€€€€€€€€{vrh„aˆ^Š[ŒZYY[`ŒbŒa`Ž\]^Ž\`Œc‹dŽ[’Y’YZ_cŒb‹bŠa‹aŒ`^‘]‘^`‘`’a’a’`“b’h’wŒ~ƒ€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~}|}|}}~}~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~€~€~€€€€€€€€€€€€€€€€€€€€€€€~~~~}|}|}|~}~}}||zzyywyvyvxvptn|u{vwvxvzu{tyuwuwtwtvuvuxuzvzwzv{tzt{tytytxuxuytyszs{rysyuwsvswuxvyvzvzv{u|v{u{uyuxtxtyrzrzryrxtxtxuyuyuyuxuwtxrzp{p}p}p|q|q|r}r}s}t|u{u|t|t}s}s|rzqypzq{p|n|o}q}s{szrzq{q}q}r{sztytysxqyq{p|o}m|gzhxoustzy~~€€€€€€€€€€€€€€€€€€}~xqe‡a‰_‹\ŒYYYZ]Ž^^_‘^’^’]‘^bŽf`[•Z“Y’X’\^`baaŽa`’_’`’]’^’^’a’b’c“i‘j”yŠ~€€€€€€€€€€€€€€€€€€€€€€€€€€€€~}}|~}~}~~~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€}|~~~~}||{{yzxzy{z{yzxywxvxvywyvwtorpzuzvwwwwxwwwvwvwuvuvuvvvxuzvzxyxywxvzvzvzv{u{u|u{szsxsxtxtysxrxsytzt{v{w{v{u{t{tzt{s|s{s{szsxtxuwuwuyu{u|u|t{szsyszq|r}q|q{q{r|r|r|t{tzs{r{r{r{r{r|r|r{s|q}p}p}q{szszr{p{p|o}p{q{rzrzqzpyqyrzrzqxkukuovrwy{}€€€€€€€€€€€€€€€€€€€€z}q}dƒ_‡^ˆ]‰\‹\]^\Z[’\’`’a’_‘`ci_•]–]“\“[’_`‘bdc‘b’a“`’_’_’_“`“_“_“_“`”d’e–s‘}„€€€€€€€€€€€€€€€€€€€€€€€€€€€€}}}}}}}}}}}€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€|y}|~~~~~}||{{yzxywywyxzxzxywywywyxxtpmptvwwvwvwvwvyuytzsxswswtvuvvwvxwxwxwwxvyvyvyvxvyuxvxwvwvvvtys{r{t{t{uyvxvxuyuwuwuxt{s~s}s}s{sytxuxtyuzuzuyuzsysysxrzr{r}r~s}s|s|sztysys{q|q|p{q{r|q|r|rzryqyq{o|p|s{t{szr{q{p|r{r{q|qzpzqyryuyxuuqonlqntruyy~~€€€€€€€€€€€€€€€€€€€€}}q|b‚]†\ˆ]‰^Š_Œ^_^Ž]]^``^aŽcja“_“^‘_^bbdŽdcb`‘`‘_“b“a”a”`”^“^’b‘e‘j‘m”{ˆ€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~}}|}}}}~}€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€{vzx||~}}||{|{{yywwtvtwvywyxzxzxxwywxtplosvuvvvwuzu{wzxxxuwtvsusvtvtvuwuvvxxwxvyvxvwwwwuwuxvxvxwxwvxvzu{uzwxwwwvvvuwvxuwtxuyt{s{r|q}q|q{szs{uytxtvswsyszszszszszs{r|r|r|rzsyqyq{p|q{pzrzs{s{r{rzryqyq{p|p|r|sztytysyrzrzrzq{qzp|p{r|t{xsykveshukvmzu}}€€€€€€€€€€€€€€€€€€€€€€~~r€c„^‡]‰^Š_‹^^_aŽ`Ž`_Ž_`‘_‘bcc[‘]’]‘]‘_bcŒdŒdŽa``_‘_“_”`”`”]“]’^‘be‘h‘e–v~‚€€€€€€€€€€€€€€€€€€€€€€€€€€}}|}|}|}}}}€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€xsyw{z|{{z{z{zzwxrunslrmurxuywyxxvxvwtplqsvvvuvvvzv|vzwxwwvwuwuwuwvwvwvvvwvzu{s{syuxvxwxwwwwvwvwwvvvvwtys{s|u|u|u|u{u{uztyuwuwtyszr{r{r{s{tzuytxtxsxszs{t{tztzszq{p|p|p|q{rzryryryryrzrzr|r}q}q|q|r}q}q}q|s|t{tzsyrzrzqypzozp{p}r~r}s|uqwgveukuouqzw~~€€€€€€€€€€€€€€€€€€€€€€rƒe‡`‰_‰_Š_Œ]Ž\_ŽcŽcŽcaŽaba‘a‘d[‘X•Z”Z’[’^_aaa‘`_‘_^^\‘^‘^’\’^’_’a’e’f’f’q|„€€€€€€€€€€€€€€€€€€€€€€€€€€}~}}}}}}}}}€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€ywzxzyzxzvzuyuyqymvhsgphpltrwuxvyvxvxupqqxvywvwvwvwxwywxwyxywzvyvyvyvywxwxwwvwtwtwuyuzvzwzvyvywxvtvsuusxrzq|s{s|t{s{s{uztyvxvxuytxsxrxrxtytxtxsxsysyszszsysyszrzr{q|p|p{rzryszszrysysyrzrzp{o|o}p|r|q|p|p{r{s{s|r{r{rzrzpzpzq{q}rq€rryrqqlsortruzz€€€€€€€€€€€€€€€€€€€€€€~€q…dˆaˆ`ˆ_Š^Œ\Ž[Ž]ŽaŒ`Œ`‹`Œ_`Žbbf\“Z–\’[]__``Ž_Ža`abb_^_‘]’_’_“b“c“c“e‘exˆ€€€€€€€€€€€€€€€€€€€€€€€€€€~~}}|}|}}~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€zy{yzwzrylyjwkxkxkwitiolmnoprrusxvxvwtpqoxt{uzwywwxxxyxzyzyyxzwyvyvwvvxwwwwuvvvwtxtytyuyuyvyvxxwwuvutvrxozozpzq|s|t{sztzt{u{u{uztzs{rzr{tztysyryryr{szrzsysyszszu{s|r{qzqyoyp|p}q{ryrwrxryqypzo|o|qzrzqzp{o{p{q|r|r{r{q{q{r|s}q}qp€rs}nxntoqqsstzz€€€€€€€€€€€€€€€€€€€€€€~p„d†_†]ˆ\Š[Œ\Ž\Ž]Œ`‹`Œ``Ž\\aŽcf]•]—_“`aŽbaŽ```ŒbŒ`Žbbb__‘_’_“_”_“`“b”b“b“cs}ƒ€€€€€€€€€€€€€€€€€€€€€€€}~|}|}}}}~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€{z{y{v}n|fzewgviwmxnunpqlulwoxuvxvxvwtplpusztzwxwxxxwzwzxywzvzu{uzuyuxwwxvwuwvvxtxsxtwtxtxuwvwwxwxuytyryqypxrys{t|uzuyuxuztzt{t{t{t|t|t}s}q}r{rzryrysyrysxtxtxt{s}r}p{o{ozpyqzq{q{qzqzr{rzsysxryqzr{r{r}q}p|q{q{r|r{qzpzq{q|r}q~q~p~rt}m{hvjsoqtrzz~~€€€€€€€€€€€€€€€€€€€€€€~oƒc…_‡^ˆ\‰[‹[Œ\Œ]^Ž`ca‘__`a‘b’\—`˜_“]]]\‘]‘]`b`Ž`_Ž^[‘_‘`’`“^”_•`”c”c”b”d‘d“x‰~€€€€€€€€€€€€€€€€€€€€€~~~|}{}|~|~}~~~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€zyzxzu{n{gzdxewgxoytxtttpuowqxuwxvywxusksruzt{vzvzvyuzt{uzuzuzu{u{tztyuywxxwvxuyuytzuzuzuzvyvxwwvwtxtxsyr{rzr{tytyuyuxuzuztytzs{s{s|s{r|p}q|rzrzryqwrvrvsusvrwqzp|p}q}q|p{qzrzq{q|p|p|q|r{tzuzt{s{s|t|s}r}r}r|r}q}q|q{rzr{rzr{r|q}q}q~s|nyjujqnnqqzz~~€€€€€€€€€€€€€€€€€€€€€€~€o…d‰a‹`‹^Œ]Œ]]^Ž_bc‘_“_’_‘bfgc˜c—^‘[YZ]_’_’beb`Ž`Ž_^aec‘a“^•`•b•d”d“e‘b’tŠ~€€€€€€€€€€€€€€€€€€€€€€~~~}|z|z|z||}}~~~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€yxzxzwxtwowmwmxlyqzvzvwturusvtvtwuyvwtopqyv~v~v}v{wzwzu{uzuzt{uzuxuxtxuyvyvxuxtxuxuywzv{v{vzvxvvuututuuwuxuxtytwuutvtvuwuxuxuzt|r|q{ryqwqxrxsytyrzoypxovsuuvtwszr{r|r|rzqyrxryqyrzpzpzqzr{t|t}sr~s}t{tzszs{r|r}q|q|q}p}q~q}q}q}q}r{sxuxpxovoqrmtp{z~€€€€€€€€€€€€€€€€€€€€€€~€s…gŠbŒ`^Œ^]Ž]^_dŽb_‘__bŽiŽif˜d–\YŽZ_ŒaŒbcŽeŽdŽc`__‘`cfŽeb‘^”]–_–d•e”g’e“r~‚€€€€€€€€€€€€€€€€€€€€€€~~}{}z{z|z|{}}~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€yxzy{yzxywyvyvyt{t{w{vyuwtwuxvwuwuxvvsooswxzy{xzwzvyvxvwvxtzszsyswtutxuyvyvyvwvvvvvvvwvxvyvxvwvtvttututvvvvvuvuuuututvsvswtxt{r|q|q{rysysxtysytzszqzqzqysytytxtztyszsysyrysytzszrypyqzrzs|s~qp€pr~s}s{s{s{r|s{rzrzr|q|r|s{r|q}p}tyuwtxpzozqttowr}{€€€€€€€€€€€€€€€€€€€€€€€~wƒlˆf‹aŒ_Œ^]^`_b`]_`bgŽb‘d—e•[ZŒ^‹`ŠbŠ_‹aŒcŒdda`‘a’a“a’b‘c_^’[•]–c”e“g’h’u~ƒ€€€€€€€€€€€€€€€€€€€€€€€~~}~{}{|{}|~}~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€|{|{|{|z{z{z{z{y|x|y|y{yzy{yzyyvxuwuvsqlttxxzvzvyywzvzuyuxtytytwuvuvuxvzvyxxywyuwuvvuwuyvyvvvsvsuttvswswuwvxvwwvvuuvtvswtxtxtzs{r|q}q|s{szs{s|r|r{rysysyrzryrxryrzr{rzrzrzszs{szszrzq|p}p}q}p~p~pqp~p}q|r|r}r{q{qzqzrytwuwuxt|r}uzvyryowowqsuqxu}|€€€€€€€€€€€€€€€€€€€€€€€€~~x~m„f‰b^]Ž^_``‘`’^“\“_’`’b’f_‘f’d\ZŒ[‹]Š_‹`Œcefeda‘b“b”a”`“b`‘`’_“^•`•e”g‘lŽwŠ~‚€€€€€€€€€€€€€€€€€€€€€€€€}~|}|}|~}~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€}|}|}|}|}|}||{|{|{|{|{}|}}~}|{ywxuwuwstlvuyyxwxxwywzvzvyuyuyvxvwvwvwuwuwvvvwxxxwwxuxtyuzvywxwvuwtxtyuyuwvwvwuwuwtwtxsysysztztysyq{q{q{sxtwsxsyryrxswswqxqypxpxqxryrzs{s{r{q|q{qzqypyo|o~oq~r}r{q|q~p}p}r|t|s}q|p|p{ozoxqwswtyt{s|tywvrtlskuossryv~}€€€€€€€€€€€€€€€€€€€€€€y~n‚d‰`]]\Ž\\]]\[‘[aŽfŽfhŒ`fŽb‹]Œ[]Ž`dfŽefŽgŽd`‘_’_“_“_“_`Ž`a_‘^”^–c”d’nŒ{…‚€€€€€€€€€€€€€€€€€€€€€€€€~|}{}|}|}}~}€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€|{|{|{}|~}}|}||z{z|{|z||}}~}}|zxyvxvwtsovxz{xxwwuxvyvyvywyvywxvxvxvxuytxswtwvvvwvyuztzuzv{v{v{v{v|u{uzvxvwuxuytxuxuwuyuzt|tztysyqzrzsxuvvvuwuxtwuvuvsvqxqyqzrzszsyszszszrzq{o|p{p|q|p}q}q~s}t|t{r{q|q|r|t|t|r|p|o|p|p{p{q{q|s}t}t}uzxusrmrlsntqsyx~~{~n‚c‰_^\[ŽZ[Ž\\\[[Ž^Œb‹gŒhia’f‘`\\`ŽcŽddceŒfd`‘]“Z”\“\’^___‘_“`”b“d’b’lyˆ~ƒ~|}{}|}|}|~|~~|{|{|{}}~~}||{|z{z{z{z|z||}|}{{yywxwwtpnqvyyxyxywxxxwxwxxwwxxxwzvywxvxtyszszuxvwvxuyuyuyuzuzv{xzw{wzuyuyuyuzu{u{uzvxwwvxuxuyt{r|q}q}szuxwwxwxwwvvvuvsxrzq{q|sztzsys{p|p{p|r|r|t~r~r~p}q{r{s{s{r{p{p{p|q|q{q|q{p|p|p{p|q|r|r}s}t|uzwxwzsyouqqsrvu|{~|~p€f‡a``‘]\Œ[Œ\]’]“]‘]]Ž_cfhkc’fa\Ž\]Ž^ŽabŽabŽcb‘^“[”\’^‘`cŽbb‘b“c”d”d“e’`’ioŽ|…€}~{}{}{|z}{}|~~|{|{|{}|~}~}|{{z{z{z{z{z|{}|}{{yywxvwtomnuvxwyxzvyvxvzv{vzwzwyvywxwvvvswsxtyuxvxvxuxvxvxvxvyvzvzvyuxtwtxszs{u{uzuytxtxuxtxt{s}m}g~c{hwmvpnwh}i|pxwuzs{r|qq~s|s{szr|o~n~oqrs~q}o|n}n{p{qzqzp{o|o}o~o~o}o|p{q{qzqxrxryr{r{s{s{sysytzrwqrsptpwu}|€€€€€€€€€€€€€€€€€}se„_‰]Œ]^^Œ]‹\Œ\\\]]^`ŽeŽec‘c’d“gb^Ž\Ž_^cdcdd‘da‘[’^adeed’c“c”e”e”e”a“ji’wŠ€€€€€€€€€€€€€€€€€€€€€€€~~{|y|z{z{z}{~}€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€}}}{|{|{}|}|{{{z{z{z{{|{}|}}|{zxywywxtponxt|u{uyuwvwwxxzxzwywywxwxuwtwsytyuxvwvywzuzuywxuxuyu{t{uzuyuyuxtytytytwtvtwtyu|s}q~k~fzfvgtirnqtk{dƒ^‡\‚hzuuzs|s~s}t|s{r{p{o}p~qrs}s{qzpynzn{n{nzozp{p|o}p}p~p~q~q}r|s{szqzrzp{p{p{q{sytztzqvnsntoqut{{€€€€€€€€€€€€€€€xj‚`†[ŠY[Œ]Œ^‹]Œ\Œ\Œ\^Ž^^Ž_Ž_Žaa’^“`—h–c‘_]^__cŽaŽaccgd’[—[•_‘accb‘`’a“d“e“d“a’ikxŠ~„€€€€€€€€€€€€€€€€€€€€€€€~}{|z|z{y{x{z}|~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€}||{{{{{|{|{{z{z{z{{||}}~~~}{zzxywxwxtoopxu}v|vzvxxxxyyzxyvxvwvwwxvxvyuyuyuyvxvyw{v{v{vzuxtxtyszt|u|v{vzvyvzuysyszszs{qjƒ`„c}ltoonpopsoxo|mi…b†_€lwvv{u}t~t~s~r}q{q{q}q}r}s|syqxqxpyp{p|o}o|pzryryr{q|q|r}s}t|t{s{p|p|o{nzozozrzs{synwmvnsrnwq|{€€€€€€€€€€€€€€zmƒb‡\‹ZZ\]_Œ^Œ]Œ^Ž]]‘]‘^^^_a’f“m”l”c‘__ac‘bb\Ž[Ž^aŽfb“Y—Y•^“`‘ccb_ad‘fb‘bfj‘s’{ˆ€€€€€€€€€€€€€€€€€€€€€€€~~{}z|z{y{x{z||~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€{y{y{y{y{z{yzyzx{y{z||}}}}}|{yywywxwxupnswvzwywxwwwxwywyvxuvuuuvuxuxvxuxuxtxuxtys{rztyuyuxuwuxtytzuzuzuzu{vzvyuxuzt}u}mdg{ostprqqssrvtzt|s~s€q€m}jxrx{w~t~t~s}s}t{tzuytytysyszrzqzrzrzr|q}q|p{q|r~r~r}r{r{r{s{szr{q|p}p}o{p{p|r|ryrtlqkqmpsnzs~|€€€€€€€€€€€€€€|€o„dˆ_‹\Ž[Ž\^^_]Œ\Œ]]Ž\\‘^‘a‘b‘e’jqŒtŠkŽdŽacŒfŒfŒecŽ_Ž]Ž`bfe‘[”[“^“_’a“`“`’_’a‘deb‘b‘e’g“f™v‘~„€€€€€€€€€€€€€€€€€€€€€€}||{|y|z{y{z}|€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€ywxvxwywywywywzxzxzxzy{z{y{yzxywywywxtqktsxwwvwwuwvxvwwwwuxuvuuuuwtwuwuxvxuxuxtyt{s{syuxvxvxvxuyuwuxuyu{t{vzvxuxuzt}q}jxnpsmulvotsttttwtytzu{v|w|vxqvqwxu|s|s|s{t{t|u|t{szsyszt{s}r|rzryrzr{p{p{p{q|r|s|s{szszrzq{q{q|r|r|r{s{t{uwxuuqpplqnqsozu~}€€€€€€€€€€€€€€~t†g‹b`Ž`Ž^]^‘^Ž]Œ]‹\Š[‹\Œ]^Ž`c’h’ntˆx„r…gŠbŒ`aŒdŠd‰e‰e‹dŽ`a‘a‘c‘d‘Y“[‘^‘`‘`‘_’`‘a‘bedcfg‘g“j•w|ˆ€€€€€€€€€€€€€€€€€€€€€~~}{}{}{|{|{}|~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wswrwswtwtxuxvywywyxzxzxywywywxwxvywwtoirruwvxvyuxuwuwvwvwxxxxwwvxtwuxuyvyvxvyuzt{t{uzvxwxvwwwwwvwvxvyv{t|tztzu|v~t~nysrwqypyoxnwowpwpyrztzw{x{z|zzwwoxrwytzszrzs{r~r~q}q{rzr{s|r}q~q|q{qzqzpzpzoypyqyr{r{rzrxrxqzr{r|s|s{s{rzsztuwtrsnsmqqpuo{v~€€€€€€€€€€€€€|€nˆdŒbŽ`a__’_’^]Œ^‰_‰_‹^Œ]Ž_Žch‘nu‰z…uƒi‡a‹]\\Ž]‹`‰c‰e‹dŽca’_‘a]‘T“X’\’`‘`‘^^abdcaŽcffjj’zˆ€€€€€€€€€€€€€€€€€€€€€€}~|~}~}}|}}~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€vpwoxpwrvsvswuxvwvwvywywxvxvxvxvxuwuvrllmvsyuzuztzszuyuxvxwyxyxzwyvxtyuxvxuxtys{s|u{uyuxuxuwuwuwuwvxuxuzu{tztzv|tn{suxu{v{u{t{t{qzoypzs{v{y|z}|}}}{yuvswxuztzrzr|p~p~q|r|r|r}r~rqqop}qzqzpzn{n{o|o}o}p{rzsxsyszr|r|r{r{q{s{twuunvjtlrrpvr|z€€€€€€€€~€v„gŒ`_]]]]‘^^Ž]]‹^Š_Š`‹aŽd‘io‰uƒ{€x‚m†cˆ]Š\‹[ZŽY\Œ`‹aŒba‘`“^’b[‘V’X‘]‘]‘^‘[\Ž^Ž`bbaŽacŽcgŽh‘w‰~‚€€€€€€€€€€€€€€€€€€€€€€€~}~}~}~}~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wmxnypzqyqxqwrvquqvqvrvrvruqvrwsxsxptkklmxwyxxwwuvuvuxuyvyvywywzvyuxtwvvvvvvuwtxtyvyuyuytxtwtvtvuvuvvwvxuztzu{q{qwuszu{y|z|y|y|z|uzmxmyqzv|y|{~}~|zvruwuxsyrzr{q|q|q{r|r|rrqq~qpp}p{pzp{p}p}q~q~p}q|r{szszrzr{rzr{s{q~p~tytvnwkwkwqswt|{€€€€~~~}}}xo…e`^]Ž\Ž[[[Ž]\Œ\Š]‰_‰a‹dŽgn‹uƒz~w€l…b‰_‹\Œ\Œ[ŒZŒYŒ\Œ_Š_‹`Œba’a’e]’W“Z“^“]“^“]’]‘^]‘a‘a‘bcbŽadlŽ{ˆ~ƒ€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€vqytzw{v{rynwitgtgshtithrgrgrhuhzd_‚[{`zqywwxwwwuvwvxuzuzvyvyuyuytxuvvuwvwvuwuvuuuvuxtysxswtvuvvvvuvutwszu}uxppuq{v|z}|}}~}}}|yzrwnvowryv{y}|~}|vqtusvryq|q|q|r{rzr{q|q~pppp~q~p{pzpypypzq{q{rzrzqzq{q|p}p}p}q|r|r~qn€p{qwmvlwlvrqxu}|€€€~|yxvxqjŠeŽaaŽaŒ`Œ^Ž]\‘\‘]^Ž_ŒaŠbŒeŒhŒm‹s„wsj…aŠ[\\\\Ž[[Œ`‹b‹_bd‘c’d‘hŽ_‘X•Z–_”`”b•a•b“b‘`‘c‘c‘cdŽbad‘k‘z‹}…€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€xq{y~}}|{uymxiwisjqmpmompkpirixi}efj}rzwwyvwvwwwwxvxvyvxvxvxvyuzuzwywywywxuwuvvwvyvytyswsxsxuxuwtvtvsxt|tzsnwp{y}|}}~~~}zzrxkulsntqyw{z}|€€~}xutyq{pzq|q}r|r{qyqypzo}p}q~q}p}q}qzqxqxpzo{p{pzpzp{o|o|o}m~n~n}o|p}q}q~o€p}myjvluorsoyu~}€€€~z~rk„eŠbŽbbbŽbŽ`_\^^_``ŽaŽcŽg‹l‰qˆr…p„i†aŠ]\\]‘^‘_’_‘aŽdŒeŒdc‘d’d’cgŽ^’Y•[•`“a“b•b”e“e’e‘edbŽa`afl‘y|‡€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€yn|z}yynxmvsrtpuqsprrrrqtqwrxsyvyuyyx|v}v|vzvywxwvwvxuwvvwvyv{w{wzwywyvxtwtvuwvzu{tztxsxsxuxuwtwuyvzyyuouo{y~}~~}{xynxjxotmspyw|{}}€€€€~yyt{q{p{q{q{q|q|p{pzp{q|q|r|qzq{p{pzryqyq{o}o|p|p{p|p}p|q}p}n}n}n|p{qzq{o~r}mziykxqttr{y~~€~}~xo‡g‹c‹aŒcŽcb`^Ž_`‹`Š`‹`Œ`Žbeh‹n‡q…lˆhŠdŒ_\Ž\]\]^_’`’a‘aaaab`_cZ“W”Y”^’_’`’c‘d‘d’d’d‘bcb‘a’c‘g‘n‘z|†€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€xq|{}zxnwnuusttttqtpuqsqtrutvuwvyvxwwyw{w{w{v{v{vxwwxvwwvxvyvzw{vzv{uyvyuwwuvuvxtzrzryrxsxuwtvtwvxyy{swpzx}}€~}zztuoyszwsqsqyw||}}€€€€zzsyu|r}p}q}q|r}r|r{rzrzrzr{rzrzqzrzrzszr|q}p}p}q|r{rzszszs|q}p}n~n}o|q|q~s|o|j|j|pyvy||~~~|}x~r†kŒfdbŽbb’`’__aŒbc‹c‹b‹dŒgŒl‰q„sƒo†eŒ_Ž[ZYZŽ[Ž\Ž_`_Ž_Ž\\‘[’Z’_`a`ŽcŽY’W’Z‘\‘__``_‘a’`’`‘bba‘c‘fn{ˆ}ƒ€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€|x}}€€~~~zzm{hylxmwputtvrvpssqsrttuvvvuxuzt|v{wywxvyuxuvwuwwvxuyvzwzvzv{vzvzvyvwvwwwvvuwuxsysxtwtwtxuxys}q|x}}~~~€€€€~~}|{vzmwqxyxzrsrrzx||~~~~€€€€~~zypxv|u~sr~q}r}r|q{q{qzq{r{q{r{q{s{rzrzp{p|p|q{q{qzqzrzs{s|r}p}n}o}p|r|s{s|o}ll€q~w||}{~v~ql…hŠf‹eŒdŒbbba‘babŽbcdŽeiŠn‡sƒs€mg…a‹^Œ\Œ\[\[]_`_Œ^‹\ŒZ[‘[’_bŽdeg\‘Z_Ž_```bŽbcb‘`‘babeiŽp‹|…~‚€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~|~~€€~~~z|n}h|jymvotstustrssqqrrusvsusvsyt{u}v|wywyvxvxwxvxuytxuxwwvyuzv|u|uztxtwvwuwvxtyszpyqyrwuwyt}i|s}}~~€€€~~{ytrvqwxw{wysntq{z}}~~~~€€€€~~|zuvx{w|u{t{r{q{q|o}p}p}q|r{p|p|q{rzryqyo{n|o{p{p{q{p{q{q{q}p~p}p|p|q|q|q|q}n}l}nzrzx|y~sƒm‡i‰fŠe‹eŒeeŒcŒcŽdccbcdŽegŒk‰p…t‚qƒj…d‡`ˆ]‹_Œ`ŒaaŽ_]Ž[]Œ^Œ_Œ^Œ^^Ž\^‘_‘aegŽh][__’^‘`‘`acccb‘d‘d’d’gloŒy‡~€€€€€€€€€€€€€€€€€€€€~~~~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~{}pk~l|nxnvouquoupsqsrrvswrxrwtxwxwyxyxxywyxyzxzvzuzuxvwxvvvuxuzu{uzuxswuxuyuzs{s{rzrzsywy{o|p}z~}|yyo{p~tzvvxtxhyp|{~~}{wwx{w|tzszrxrwqyqzp{p|q{rzr{q{pzpypxpyp|o~n~o}p{pzp{p{q{r|r}q}p}q|q{q{q{q|o|nyouswv{s€m…kˆh‰fŒdŽddddd‘feŽeŽeŒfŒfŒgŠl‡s‚w€qƒi†bˆ]Š]‹]Œ`Ž_Ž_`^^Ž]Œ]‹^‹aŠa‹aŒaŽ]^_‘^‘eŽiŒe‹\Z^‘_’`bŽaab`]\‘_’c’a“e‘kŽm’x‰€}|~~|~p€i€hizlvmvlvluntqsstuvwtxswuxwxxwywxvywzxzyxywyvywwwtxsxtvvvyuzvyvxvwvywzv{vzuyuxvxxz|xzq|x~~~|{txuzt{txvsxqyjzq}{~~||syv|v{t{r{qzqyrxsxsxsxryrzrzqypyoyoyozp|p}p}p|qzqyqzq{r{r{r}q~p~q}p}p|q{rzq{r|qxvzu}qƒm†k‡j‡h‹ee‘eŽfffgŒhŒhŠiˆjˆjˆl†q‚u‚p†g‰b‰]‰\‹[ZŽ\]^\[Ž\Œ^‹_Š`ŠaŠ_Š^‹_Œ_Ž_`‘]cŽjŠa‹]‹\^^‘`Že‹e‹ec_[\_a‘`‘dl‹yˆ}ƒyv||€~}{r|l}j{ixiviwhwivmtqsvsxtxtyszu|v}xzwwwvxvyvxvwvvxvxuwtvvuwuwwvyuyvywwxwwxwytyuyuxuyxz{yn~v~~€€€€€€€}|xys|s{szsxuvxsvkxt}|€€€€€€€€||mzp|s{r{q}p|q{rzqzqyqyrys|r|q|q{pypyqyqypzp{q{rzrzr{q|q}q}r|r}q|q}p}r~s|t{o~m€o}u|sp„mˆl‰k‡j‡jˆjˆiˆi‡jˆj‰j‹lŠnˆp…p„r‚tsm†e‹`Œ]Œ]‹\‹\Œ[Ž\]\Ž\ŒZŒZŒ]Œ\Œ^‹\Š[‰^‰]Œ]Ž]‘[“\’]`Ž^^Ž]]‘^‘begŒfŒca^Ž^cŽdbfl‰|„€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€{t}{€~|ysxmylxjwhuhtksmrprrustvuxu{u}u~v|wxwvwvwuxuwvwvvwuwtvswtwuyvzvzvywywywzvzuyuyuzuzv{x|}sp~z€€€€€€€€~|xxxtwxr{q{rztyvwqoxw}}€€€€€€€€||k{o|q}p}o}o{pzp{o|n}o|p{r|r{r{q{pyqxryqzq|q|r{s|r}q~o~o}p|q{q{q|q}p}r|u{t{p|l|r|t|p€m„mˆm‰lˆk…l…n‡mˆnˆo†o„p†s…v…wƒxƒwƒtƒk…dˆ_‹]]]Œ]Œ_Œ]Ž_^\Ž[ZŒX[ŽZ[ŽZŒ\‹^Š\ŒZX‘X’Z’Z‘\‘_’^‘^‘]]bŽdŽgŽgfeŽcdŽfedgŽlˆz‚€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~y~}€~|zryk{hzgxfuhrlqorqspvqwrxtxww{v|vzuuvtwvwvwvxwxwxwvvvwtxuyuzuyvzuyvzv{u|t{syuxuwuwvxzs€h~t~€€€€€€€€~{{n{q|s|r}q}r}s|vyrsyx~}€€€€€€€€{|o|s}r|q{p{qyqyqzo|o}o}o{rzryryryqypzp|p~q}r|szr{r{p|o}o}o|p{p{o{o|p|q|r{qzpvpsvxv~qƒo„p…q‡rˆt†t„t„s†u…wƒwx€{€|€}}u…jŠc‹_‹]‹\^Ž_]^Ž\\]]Ž[Œ[Œ[][[Z\‹\Š\‹\[[Z_ca][Z[`Žcgf‘f‘dbbŒcŽc‘c‘gkŠx„€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€}~€€~|{o|ge|fzgwhvjvkvlulvlxkykyqwxwzwxvsvswtxuwvwxwxwxwwwvuxuyuysyszs{r|t{szsysxsvtvuuuszn{s~|€€€€€€€€~~r}rztytzs}s~r~s}wvvr|z~€€€€€€€€{|r}t}s|r|rzrypxqyqzq{qzqzq{q{qyqxpzp|poo|pyqwrwqyq{p}p}p}p|p{o{o{p|o}n}k{nvpsvyvsƒtƒu‚w‚x‚z‚{€{~{~{}€}}~~€x…n‹fŽ`Ž][[Œ^‹_‹^‹][\]Œ^‹_‹`Œ_Œ]Œ\Š]‰\‰^ˆ^ˆ^‰^Œ]]’f‘jŒgŠ`Œ[ŽY[]`ded‘e’b‘`^`‘a’b’hŒe‹v…€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~}{|q|g|dzewexhvivhufrfrewb~^e{qwvuvtuvtvuwuxvvyv{u|tzuxvxwxvxuytzs{tzuyuxtvtuutuuvvxuzj{u~~€€€€€€€€€€z}su|tytxtzt|s|r|ryrv{{€€€€€€€€w{r|t|s|r}p|qzqzq{r|r{qzq{p}o~o}p{o|p~p€pq}qzryryr{q{q|q}q|p{qyqxpzo~mm€p{pwnrsxt~tv‚wxy{€~€~~~~x†o‹gŽb`^]]_‹^Š\Š\Œ\Œ]]Œ]Œ^Œ^Ž]ŽZZŠ]ˆ^‡`ˆa‰`Œ[_‘kŽq‡mˆeŽ]Y‘Y‘]‘`cdd‘c’e‘cb_‘`‘_“`’gŽe‘x‡€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~}~~~~~}|zzvxrwowmvjwivjvjuisgsfudy_~]~c{mxsvuvtvtvuxvxwxyvzuytxuwvxwxwywywyvxvvuvtwuvuwtwvwzv{v}|€€€€€€€€~~r{t}u|tzuyuyvzu{uzrutu||€€€€€€€€€€u|oqr~r}q}q|q}q}r~q}p|n{o|o|p}q}q}q|q|r}q|r{r{r{q|p{p|p}q}q|rzqzq|pp‚rt~p{lxs{vuvxy€z€|~€~~}{~tƒkŠeb````‘`^^Œ]‹^^Ž^Ž^_Œ_^Ž]\ŽZŒ\‹\‹]Œ]]‘e“sx…t…lŽd“]’Y‘Z^_‘a‘c’d’c’c‘_‘^’\’a_a‘io•{ˆ€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€}}}}}||{|{{zzwxuwswrwrwtwtvrtmvhvdubsdsfupwwvwtvutwtytzvxxvzvxuvuvuzv{w{vzwxvxvwuvtxtytztzuzzr}y~€€€€€€€€€€~y{rxuwuyt{u|u{u{v{wvunwt}}€€€€€€€€~~vxr{sr}r|r{r|r|r}r}q~p}o{p{pzpzqzqzr{r{rzszszs{r{q|p{q{p}q~p~p}p|p~p€r‚tq€n€ks~v€uuwy{€}~~~}||v~n„h‰c‹b`__`__]]Ž_^\^Œ^Œ`‹`Œ___Ž_Ž_X‘X’`”pxˆ|ƒ{ƒrŠi‘d“a‘_^Œ`_‘]’a’a‘cb^‘]“]“a’b‘dlŽv‘{‡€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€}||{|{|{|{{zzwyrxnwmwowsxtvqviy`{^xaqgnnnyt}t{syuuxryryuvwuxvwvvvvvxvywyvzvyuyuxuvtwsxtxvyyx{v~|€€€€€€€€€~~s{szsxsyszs|s|tyvuymyhzu~~€€€€€€~|tut|ut}s}s|q|r}r}r}r}q|pzpzpzpzp{q{r{s{s{s{r{q}p~p~q}q|q}p|o{o{p{p|q€q‚s€q~p|pyvzx|vvxƒz‚|~€~~}{}v~n„g‰dcŽbba`^Ž^Œ^Œ^]Ž[\‹\Š[‹[Š\Š^‹_Œ^^Ž^_‘^’]•i”v‹~‚~€}€yƒn‹gb‘`aŒa‹a‹__ŽdŽdŽdcaŽ_c‘c‘b‘c‘jv{†€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€|{{z|{{z{z{zzvznyiwhwkwmymyiydy`z_{_ucnjmvs{s{rytuvtxuyvwwwwvwuxuxvwwuwuvwvxuxuvttttttuuxv|p~x~€€€€€€€€€€~x{t|t{ryryszs{r|s{uxwlwj{x€€€€€€€}|pyqrq~q~r}q}q}r}s|s{qzpxpwpxpyqzq{s{s|r|q}o~o~n}o|p|q{q|p}o}p|r{s{u}u€tq~r{tyz{|}z{€}~€~~z}t€m„g‰b‹``Žaaa_‘_^_Œ`Œ_Œ^Œ[‹]‰\‰]‰\ˆ^‡`‰`‹^ŽZV’W“b“r|„€€|‚u‡kŽc‘`‘^`a‹`Š_cŽfca`b_‘a’a‘_‘`‘gq”z‡€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€zy{z{z{z{z{yzuznxiwhwhwfyeyazaxbybybugolnvtzszryrwutytzuzvywwyuzuyuvvtwtvvvvvwvvvuuuuvuxwywv||€€€€€€€€€~~q{t|t}s|s{s{r{r|q}rq{pw{|€€€€€€€€~{|k|o~q}s{r{s{r{s{t|s|r|p|o{p{p{q{r{s{tzs{r|q~p~p|qyrxsxrxqzq|q}r~s~t{wyw{v}s|r{tz{}~}~€~}~x€p„hˆcŠ`‹^‹^^``‘_‘_‘_‘`ŽaŒ_]\Œ]Š_‰`‰`Š]Š\Š_Š^‹^Ž[\“kx‡~€€€€~y„pŠha]\Ž_Œa‹`aŽfh’d“`“`‘`_‘`‘`_`fŒk{…€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€zyzyzy{y{z{zzvymwivhujvkvhwa|aydxfwhtkqnrvuyuyszrzuwyt{s{tzvxxvyuytwvuwtxuyvxwwwwwvwuvvwxt|y~~€€€€€€€€€€~~~wzwytzs{s{s{rzr{q~r‚qq}{}€€€€€€€€~~yzg}p}vzvxuyszr{s|s{r{q{p|p~q~q}r}s}s}s|s{r|r|q|q{ryrzrzr|q|r}r~rrr}syu|v{szpzpyy{~~~}}z|tm…hŠdaŒ^‹^Œ^Ž^‘]‘]‘^’_‘_`Ž^]\‹\‰^ˆ_‰b‹bŒ`Œ]‹\‹ZZb“q|„€€€€€€€€|„t‹kd_\]_^[‘^ef’c”^•`“a’a’`“b‘abiˆm‡|‚€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€zyzxyxzx{z{zzwynxivivnwtxsyj{exgwkvnvqsmutvxuwtxtvwsypzp{sysxuvuuvtvvvwuxuxvwxvyvxvxvwvwyl}y€€€€€€€€€~s~szuxtyr{r|q{rzszu|w}vxww}}€€€€€€€€~wzg|o~t{uzu{s{r}q}r}r}q}p}q}r|r|r}q}q}r|s{rzrzqzp{q{p|o}p}p}q}r}r~r~r}t{s~u|rxovpsuu||~{}ung„dˆcŒbcŽbbŒa_]]^’_’^‘^^^\‹]Š]Š^‹`Œa‹`‹^Š]Œ`nxˆ~€€€€€€}€y…p‹iŒd‹aŠa‹ba]‘X’Zaba“]”`“b’d‘c‘cbdŽm‹tŠ|ƒ€€~~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€ywxvxvzxzyzyzxzrxnwovrxwzyzvysyuywzyzxurtuvxvwvvvrxozl{m|qztyuwtvuvuvvvvwvvvtysztyvxwwyvyv}}€€€€€€€€€€~t~v{v{t{s}r~q}q|uzxy{v{m{v~~€€€€€€€€€€~}uxnzp€p€q}t|t{s{r|r{s{r{r|r|s|s|r{q{qzryrxrxqzq{p{q|p|p|p{pzqzq{r|q}q}r}r~q|nymvnrrsyy|}y€s‚i‡dˆa‡aˆaŠbcŽc‹cŠd‰bŠ`Œ_Ž^__```‹_‰`‰_‹_‹`Š_Š]‹\gŽt‰}‚€€€€€€€€}y‚rŠidbŠb‰c‰c‹a\[`ŽdŽgŽe‘`“`“adŽeŽdŽcfŽm‹z‡}~~~~~~~~~~~~}~~~~~~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€vqupuqxvyxyxzyyvxswuxwzx{z{z{z|{}||||zvqvtxyxxyvzqzkyjxnxsxvxvwuwtwuvwwywyuyuzu{u{vyyv|m}v~~€€€€€€€€€€~v}r|w{vzuztzr{r|t}v|yzzvyo|y€€€€€€€€~|stqys~s}s|s|s|q|q|r}t|r|r|r}r|r{rzqzryrzr{q|p|q|r|s|r}q}p}p|qzqzrzr{q|p}tzrxmunspqtuwzt€m…h‡eˆeˆdˆdŠbabbŽcŠeˆeˆc‰bŒ__`Ž``‹_‰^‰`Š`ŒaŒ_‹]Š]Šl‰z„€€€€€€€€€~|y€s…k‹fŽba‹`Ša‰bŠ_ŒZŽ]ŽdŒeec‘^“\”\’addŽdfmŠ{…~€~~~~~~~~~~}~}~}~}}{}|~~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€smriskuqwtxvyxyuwrwsyx{z|{{{|{}}~}~}|{wqxryxywyvypxkwhvhxjznytxvwwvwvwwxxyxzyzw{v{vzyw{m|x~}q|vzvyvxuxtxsys|t}u}w}xxxt}{~~~~~~}|ruozs|s{s{r|q}o~p~r}s}r}q}p}p|p{q{rzszs{r|q}n|p}r}r~s~r~p~o}q{qzryqyq{q}s|r{oyputtwys€lˆgddŒeŠf‹fee‘efgŒgŠh‹gŠfŠcŠa‹`Œ`_Œ_‹_‹^Œ``^‘bpˆ|‚~~||y~t‚mŠfŽdŽccŒ`‹]‹_]ŽZ‘\‘b‘a’a’a‘`[‘Z_bdcfŽlŒz†}€~~~~~~~~~~~}~}~}~}~}~}~}}}}||{|y|{}}}~}€}€|}}~}~~~~~~~~~~rksgtitltnuqxuysxowryw{z||||}|}}}}~}}{ttsvwyvzuzvxuuuoujuhzjzqyvwwvwvwxwxwywzwxyxzw{xxzv}}~}|u{wzu{t{t{s|t|s|s|t|w{ytzu~}~~~~~}|{qxm|q|szsyszr|p~o~p~q|q|q|p}p}p{q{s{s{rzp{p|o}p}p}p~q~p~o}n}o}o{oznzn{o}p~l€k~lyuzun‹i‘g’f“f’f’f’f’f‘ghŽiŽiŽiŽhgfŒf‹e‹d‹cŒaŒ`‹^‹^\^“f‘uˆ~~~{}xu…nŒgbŽbccŽ`]]‘]‘^“]’_’^“]’_‘`Ž]]Ž`Žddc‘e‘kŽz…}}}~~~~~}~}~}}}}}}}~}~}~}}|}z{y{z||}}}~}|€{|{||~}}~~~~~~slshuguisktlvpwowmxqyx{z|{}|}}}}}}}}|{vutxuztztxvxxxwvssprtrxvxwwwwxwxwywxvxwxvxvywzyu|y~€€€€€€€€€€~}syrzuzs|s|r}r}s|s|t{u{w|ys|w~€€€€€€€€}|znwn{s{szsysxsyq{o}p}p|qzqzqzr{q{r{r|q}o|o}p}p}p}p}o}p|p|p|p}o}o|o{oznzn}m€jjp|wq‡khg‘g’i“h”h’hhŒj‹jŒkjifddŒgŠh‰hŠh‹geŒc‹bgŽq‹z…€€€€€€€€€€€|€x€t„oˆicŽ__Ž``‘b‘b‘`’_‘a``_‘]’]‘^^Ž`Žbedc‘fjŠvƒ}~}}~~~}~}~}~}}}}|}|}|}{{z{{|}}}}}|}|}{zzz€z{~{}}}}}~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€rksgvixjvhthukulukvpxw{z||}|}}}}~}}}||wutwuytxtvvvywxxuvtptnvtwxvxwyxyw{wzuyuxtxtxxx{p}y€€€€€€€€~~t}vzvxuyt{s|r|s{s|t{v{wzwt{y€€€€€€€€€€~}{znzr|t{s|s{rzqzp{p|q|q|q{rzq{r{r{q|q|q|p{p|p}p|p|o|o}o}p{p{p|p|p|q|r}q~omol}t}w‚mŠfe‘f’i“k”k’jjˆj†l‡mˆm‰lˆjŠh‹ggŒiŠjˆk‰lŠmŠnˆp†v…z‚~€€€€€€€€€€€€€€|x…rŠmjŽd`]^_`‘a‘`a‘abac_‘[“Y’\Z]`Žcdcfk‡v}}~~~~}~}~~}}}}||||{{z{y||}}}}}}|}|}{}{}z}z}z}{|{|{}|}}~~~~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€shuewjumrjsiuhvititlvryx{{~}~}~~~~~|{vuqxuzuxtwvwwxvxsvsprkrnvuvwvxwxwywyvwvwvxwxyw}w~}€€€€€€€€€€{{t|uzwyvzv{t{s{szt{uzwyztzr~{€€€€€€€€~}|wxi}o€qr}r}r}p|p|p|q|q|q}r}r|rzrzr{q{r}q}p|q|p{p{p{p|p}p}q|p{p{p|q}q~p€m‚nuzqxw{w€m‡gŽgikoŒn‰l†j…k…m‡n‡n…m…m…m‡lŠlˆm‡m†o†r†uƒz}€€€€€€€€€€€€€€€€€}wƒq‰mŽhfbŽ_^_aŽcŽbŽ`Ž`ŽaŽcbŽd`\’\‘]‘Z‘]‘_abŽbŽeg‰u‚~~~~~~~~~~~~~~}|||{|{{y||~~~~~~}~}}|~{~{}{}z|{{{|{|{||}}}}}}~~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€ukufsiqnsqvpwkwkviujunwszy||~}~}~}~~||txrzuzvxuwuxvysypwotpnqnwswuvwvxvxvwwwvyv{w}yy~{~€€€€€€€€€€~u{ryvxwyv{u|s|r|s|t}w|xxyqzu~}€€€€€€€€~}|szfl€n~o|p{qzq{q{q|q}q~p€qqq|q|q|q}q}q}p|q|q|q|q|q{t{s{s{q{p{p|o~o}o~o~np~m}vx„mŠijlŒn†p„oƒpƒm‡lˆm‰nŠqˆr‡q‡r‡q†q„qƒrƒwƒ{}~€€€€€€€€€€€€€€€€€~xo‡jŒhŒf‹bŒ_Œ]]^`bcbcŒbŒcŒ`Ž^‘_’^’`‘]‘\‘`‘`‘baadcŽp…~~~~~~~~~~~~~~~}}|||{}|~}~~~~~~|~|~||{~{}{}{}z|z{{{{||~}~~~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wqvmwoyv|z{xztxqvntntpuryvzy{z{z|{}}}|syq{uzvyvwvwwxuxsvpqsotqyvxwwyvzuyvxvwvxuyv{yt}z€€€€€€€€€~~qs{vyvzu{t{s{r{szt{v|v}uxxy~~€€€€€€€€€€~}|rym|o}o|p|q|r{szsysyr|q~p€qq~q|q}q~p}p|q|r|s|s}r~q~o}q|q{q|q|q}q}p}p|q|s{s~o€it€z‚o‰iŽijkŒkŠjˆk‡k‰lŠo‰s‡v„x„w„w…v…x‚z}€~€€€€€€€€€€€€€€€€€€€{~q„h‹ecŒa‰^Š]Œ\Œ^_Œ`ŒaŒabŽb`Ž`Ž^[’`’`‘]‘[ZŽ^Ž`edehŽgŒp„~~~~~~~~~~~~~~}|}{~}~~~~}~~~}~}|€}€{{~{~{~y|yzzz{|}~}}~~~~~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wsxqzt|{~~}{{xzuvtututvryszvyvyuzx{{|zrwo{t|u|uywxwxuxssqmtluowtxxwzvztytxtwuxuwwwxr}|€€€€€€€€}tt{uxuyuztzsysysytyvzwxvt{z€€€€€€€€€€~|zquqzr}q}q}q}q}s{rxrxrzr{r}s}r{r{q{r|r{sztztzs{s{q|p|o|o{p{r}r}q}q}p}q|r}s~u~qk€r~}~vnˆkŽjjŽiŒiˆj‡k‰oŠs‡vƒxz€|€}€~€~€€€€€€€€€€€€€€€€€€€€€€€€|~t~j†eb“a‘^Ž]Œ^‹_‹`ŒabcdcŽbŽbŽ_^‘]‘`bZYWŽYŽ^Ždee‘ihq‰~~~~~~~~~~~~~~~~~}{}|~~~~~~~}|}|~|~{~{~{}{{{z{{}~~}|}}}~~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€toumwrzy|{{zzwxuxtxvxvwswrysytyuywzz{yqvn{t}u{uzwyvyuyrurmskvmwrxxwzu{tzsxswsxuzwv{x~~€€€€€€€€€€~ts}vytxsytytysztzszs{uzxqyr}|€€€€€€€€€~}zzlzo|q{q|q|q|q|r{q{rzryrytytzszrzrzsztzuyuyrzq{n|n}p|p|qzq|r}q~p~p}pqqp€op~mt}~~{t„m‹jjklˆn†o…s„wz|}€€€€€€€€€€€€€€€€€€€€€€€€€€€€}~wmƒeŠbŽa‘a“_’^]^Ž`ŽabcŽdcab_^__\‘\“\‘YX^bc‘c“f”f“o‹}~~~~~~~~~~~~~~~~~~}~z~}~~~~}~}}}}}}|~}~}~|}||||}}|~{z€z€{€|}~~~~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€sfuhvnxtywywxuwswswuwuvtwuyvywzxzy{z{yqum{s}u{vzvxvxvysyqxquttvtxwyywzuztyuwvxw{ys|x~~€€€€€€€€€€~€l‚o}tzuysyszszs{s|s|r~u|xs{v~}€€€€€€€€€€€~~}wxm}p}qzqyqyrxqyszr{s{sysysyszs{s|s|s|s{tzszrzpzp{o{p{p{q{p{p}o}p~pp€q€p€p~p{rzr{u{~~y€q‡mŽlmŽoŠs‡vƒz€}~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~xƒo‰ieb‘_’_’_‘^^Ž`ŒaŒdeŽddŒcŒa`aŽaŽ`_^’_’]‘Y’Z‘`db‘`”d•d’o†{|}|}}~}~}~~~~~~~~~}}|~z}€€~~~~~~}}~~~~~}|}|}}||{|{}z}z}{}}}}~~~~~~~~~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~~~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€uavewjxpxtyuyvzwyvwtvuwvzy|{||}|}||{zyqvlzr{syuxuwuxuys{q{oxqusrxvxwxxwyvyvzv{yz~q~{€€€€€€€€|r€q~s{txsxsysyuzv{uzt{v{wtzx~€€€€€€€€€€~}|ttpxs}r{qypypzq{szszsztzszs{r}s|s{s{t{sztytytwrwrxqyqypzozo{o}p}p~p€oo€pq~q}s{s{t|}~x‚s†q‰s‡w„{‚}€~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€zƒrŠj‘h“eb_]Ž^^_‹a‹bŒcŽdŽdŽcŽbŽa`c`Ž]`’b’^ZX’\cec`”c•b’o‡|}}|}}~}~}}}~~~~~~~}}|}{~€€€€€€€~~~~~~~~~~~~~}~}}~|~|}|}|}}}~~~~~~}}}}|{€|}€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~€}€|~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€ykxmwoxrxsxuzy{z{yzwywzy||~}~~~~~}}|zyqslwrwtuutuvuyu{uzryosopmrrwtyvxwwwwwyvzzy~v}€€€€€€€€€€~v€s€t|szsxsxsysyuzuzuztywwvs|{€€€€€€€€€~~|zrvszss{swsvrxrzsysysyszszs|r}szsxsytysysxsxsxsxsyrxrwqwqxqyr{r{q|q}o~p~p~q~q€p}s|q{z}~~|~{}~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€|t†iŒdb`Ž^‹]‰\Š]‹^‹\‹_Œ_ŒaŒ`ŒaŒ`aaŽ`ŽaŽ]agŽcŽ]ŽZ[`efb^“b’a‘oˆ}}|{}|}|}{|{}}~}~~~~~}}|~€€€€€€€€€€€€~~~~~~~~~~~~~~~€€€€€€€€€€}~|~{~yy€y€yz~|~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€}z‚{~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~z~{}{{yywywzz|{|{{z{y|{}}~~~}~}}||{zxqmpmupwqxswwvzv{vytwrprnprsuuxvvxuxvxwxwzu}z~€€€€€€€€€€~ns|vytyswtvswtxuytyszt{yr{s~}€€€€€€€€€€€~~}zyqwuyu{vxuwtwrxrxsuttuvsyr{r|s{szs{rzsxswsvrwrxrzryqxrvqusvrxszs{r|r{s{s{s|s|s|s{uxqqvv~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~x‚oˆg‹c‹^‹]‹\‹]‹]Š^‹_Œ[\]^Œ^Œ^‹^Œ_ba\aŒi‹jŠeŒ`^`Žcee_’^“dfpƒ}}}|}||{|z|{}|}}~~~~~}~|€€€€€€€€€€€€€€€€€€€€€€€~€€€€€€€€€€}~{}z}y}x}x}x~vw€y€}€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~|yx{€~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~}|zzyzy{z}}}||{{{|{|{|{}}}}|{zxqnomurxrytxvvxtwrvruqqsqurvuwvwuwtwsxszr|l~y€€€€€€€€~~s~uyuwtxrwrvtwtytysytzv{{q}v~€€€€€€€€€€~~}|vupwwwvwuwsxrzpzpyqvsvtwszr|r|t|t|s}r{rzswsxrxqzq{qzrytxsxsyrzr{q|q|pzqyrzr|s|sztytyrptr||€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€{s†kŒfb^]]Ž^Ž^Œ`Œ_Œ[Œ\^_ŒaŒa‹a‹bŒbŒ_ŒaŒj†j‰gŒdŽa`ŽbŽbŽcŽd_“a“g‘iŒpƒ|}}||{|z|{|{}|}}~~~~~}~}€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~}|{{z|z|y}y}x~v€t€u€x€|€~€€€€€€€€€}y€wux€}€€€€€€€€~~~~~€€€€€€€€€€€€€€€€€€~€~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~||xytxuyx|{|z{yzyyxyx{y||~~}|{ypulyszvyxxwxvxtwpwpwmtqptqstuvuvvuvtwvyw}p~{€€€€€€€€€€{{t|tzrzrzrzsyuyuyuxuxwyzzzs|z~€€€€€€€€€€~~}{suqxuvtwrwqwrwrxryryszs{r|q|r|s|t~rq~q}q{q{qzqyrzrysztztzt{s{r{q{q|p{nzozpyqyrzq{syussp{y€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~xƒnŠgcŽ`Ž^^^Œ^Œ]]Ž]]_Œ`‹a‹bŠa‹aŒb_a‹i‡mˆhc’aaŽcbabŒcŒaa‘edpƒ||}{|{|{}|}|}|}|}}}}}}~}€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~}}}|{|z|z~yxv€t€s€tx€{}~~~~~~~~~~~}}}~~~~€€€~zw„u„tƒuz€€€€€€€€~~}}}}~~€€€€€€€€€€€€€~~}€}€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~}}|}{|y{rymxpwsyxzy{y{y{y{x||~~~~}|zrvpyuzwzxzwywwtwrwswrqtktosttyuywwwxwxyz}y}€€€€€€€€€€w~t}t{szszuzvyvxvvvuvuxyzyzu~|€€€€€€€€€~}{ypwq{uxtwtwtwsvsvswsytzs{q{qzrzs{t}r~r~q~q}p|p{qyrvsvsxtzt{tztyrxrxryrzr{r{ryrxqzpytxvxsryy€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€}yr…k‹fŒcŽ`Ž]]Ž^]^Z]]Ž_ŽbŒca_Ž^\‘c’ilŠlŽi’e’bcba`aŒa‹acŽed‰r~{z|z|{}||{}|}|}||{|{|{}|~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~~~}||z{y}x~w€u€s€q‚qƒsƒw€z~|}{~}}€}}}}}|||}}~}~~~|~x‚t‡rŠq‹r†x‚~€€€€€€€€€€~~}|||}}~€€€€€€€€€€€‚~„}…z„y~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€zsysyuyuwqwnvovqxuzy}|~~~~~}~~}{{rwpzxyxxxxvyuzsysxtwttuqsrsttwvxvwwwwwxy}|~~~su|tzszsztztztzrzsztzxy{u|w~~}|xxl{r}uztztzt{tzs{t{t{t{s{q{p|q|r}s~s~r~q}p}o|pzpyqxqyryr{s}s|r|qyqwrwrwsysztysyqzoxr{s}ovwy~€z‚u‡o‹ieca‘_‘]]]’\“_’_‘_‘___ŽbaaŽ]ajlŒjŽig‘c`aa‘a^]`ŒbŒe‹gˆfƒt|{z|{}|}||{|z|z|z|z|{|{|{}}~~~~~}}{|y|w~v~t~rp„qˆq‰s„w~{}{|‚|ƒ}‚}€}|~|}}}}~}~~}zu‚q…pˆo‰r†y‚~€~~}||}|~~€~€~‚}…|‡yˆq…r€|€ultnvrxuxtxqypxqysyw}}~}|{syo{vyxwwwvwuwtvtvuxtyqxrurrsuvwvwwwxwyt|{}}s~t|tytxtxtysyszqzrzu|yx|t}{~~|uxo}t~u}t|s{szszs{s|t{t{szr{r|r}r}r~r~r|r{qyqxpypyq{q{r{r{q|pzpzqyrxrwrwrzs|t|s|q|nzq|s|quxy~~z€u‡nh“d’`‘^]]]]\\_aŽba`^_Œ]\`iljhgeŠdŠc‹dccŽa`_`dŠi‡n‚x|zy{z}{}{|z{yzyzy{y{z|{|{||~~~~}}{|z}x}x}v}t~r‚r‡r‡t‚x~}~}}}}~€~}|}~}~~~~~}~{}y~u~r~rt‚w‚}€}~|~z}€‚~„{…xˆvˆr‡px~€vqvrwuywyv{s{rypwowr{z~~€€€€€€€€~~~}|{xwtvvuwuwtwtxtwtwtwtwtvqwpyqwswuvwwvzu{r~{€€€€€€€€€€~~xyr|r|tyuwuwuyuyuytyuywy|s}s~}€€€€€€€€€€€~~~}{pwnzu{tysysxrxrxsxsyt{s{t{sztzt{tzsysysyryryryrysyryszr{r{qzpxpxqyq{r{t{u{v|t{t|q}n|r|syus{y€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~y}rl‡heb_^]]][‘[‘[^Ž`Œa‹b‹_[[\Œ`‰h†hŠhŽf‘ccŒb‰a‰eŠcabba__hŠs„{~}~{}y}y|y{xzxzvyvzw{x|z|z|z}}~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~~~~~~}|}{{y{xzu}tt€s~u|y}~~~~~~~~~~~}}}}}~~~}~{}y}x}u~t~s€u€y€~€€€€€€€€€€€€€~~~~|€y{~€€~€~€~€~}‚}||}}~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wswsyvzxyvysxqxoxqxs|z~~€€€€€€€€~~}||{yutoumumtsuwwxwwxwyvytynyj{kzlzpxtys|r|u~}€€€€€€€€€€~~rvrxtwtuuvvwvzw{wzvyxx{skv~€€€€€€€€~~~|{n{m}q|rzryqyqyryrxrzs|q}s|szsysxsxsxsxryqyqyrzsztzsysxryrzrzswswsxp{r~t~t|u{tysyr{q}sztvvr|z€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~y€o„gŠcŒc‹b‹a``^]]‘]’]“\’]’`aŠ`Š\‹ZŽ[Ž`j‡m‚i†g‰eŒbŒaŒ_Œ_‹a‹aŒccbŽ^]aŠpƒw}}~€€}~{}z|xzvzuzuzuztzuzwzx{y}}€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~~~~~~}}|z{w{v|w{xyxyw{x~{~~~~~~}~~~~~~~~~~~~}~{~{~z}y}vts„tx€~€€€€€€€€€€€€€~~}~z~x€y}€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wswqxuzxyvyqzq{s{v{y}|€€€€€€€€~}|{ywrrmvovowrxuyuxvywxwyvxowkujsmttuwvvzn|v€€€€€€€€€€~qxuxtxtxtyvzv{w|w{vzwy{n€j~z€€€€€€€€~}yxoyqzqzryryq{p|q|q|p}qp~r|r{qysxsxsyryqypxoxoyqzrzrzqyrxrxrwrvqvpxp{q|r|szsyqxrwtvuvsvssur}{€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~€y‚q†g‹da``Œ_^Ž]\Ž\\‘[’[“Z“[“\‘_`^^ahkŠk‡h‰f‰dŒ`_]Ž_a‹aŒbaŽa‹a‰f„q~y{zw~|€€€€€~}~zx€u€r~l{mxrxxzzz{|}€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~}||y|v}w|w|x}w€w‚xz{}~}}~~~}}}}}}|}z~{{{}y~us‚s…v‚{€€€€€€€€€€~~|}x}ww€z~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€xuwsyvzxzv{r{s{w|y}|€€€€€€€€€€~}|{ysqmvs{vyxwytysytwvvwvvvptlpnlupxtxwvxp{y€€€€€€€€}~vwvvtzu{uzuywyw{w{u|vyymzn~|€€€€€€€€€€€€~}|tumzsxtvsurvryrzqzqzq{q}q}r{q{qzrysysyrypyozp{o{qyqyqyqyryqyqxownwnxpzqzryrxrwqyqytxuuuqulwo}|€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~€z…r‹kŽecŒb‹a‹`‹^Œ\Œ[Œ[[[Y‘Y‘X‘Y‘Y’Z“]’]‘^giŠg‹eŽdcb`ŒbŒ`Œa‹b‹bŒb‹_‹`‡gƒuz~y{zv~|€€€€€€€€€€~€}‚zƒs€i~o{vwzs|u~x~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€}~{}y~y}y~x~x~z~{}y|y}|~}}~}}{}{}z~yyz€{€zxus‚t‚w€}€€€€€€€€~}}z|w|v|v~y~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€xuxtywzy{x{v{v{w|{~~€€€€€€€€€€}|zxromutywyxwyuxuwvuxuyuxvvspsppvpxvxytzr~|€€€€€€€€€€~||xyvxuzvyuxvvwwwxwywzzu~h|t~~€€€€€€€€€€~~}|{zn{o~vyvvuutvswrwrxrxryq{r|r}r{szsytysyrzr|r|s|r|r{p{pzrzsyrypypxqvrusvsvsxqxqxpyq{s|vywrzi{l~{€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€|ƒv‹n’h’dcŽbŒa‹`Œ^Ž]Ž]^_]Z‘Y‘Z[YZ_^e‹iˆi‡gŠc`^Ž^_Œa‹`‹aŒabdŒe‰f„kzwzx|x{yr}z€€€€€€€€€€€€}z~tzl{swutytzr~p€vx~€~~~~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~}~|}|}z}z}{|z{y{x{z||}{z€zxxxyyy€z€y€x€wuƒvƒy‚}€€€€€€€€€€~~|}z{w|v|v~x~}€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€zxzwzx|{}{|y{w|v|x}{~~€€€€€€€~~||yxrrpuuywxwwwwwxvzuzvyvyvvsqsopqquwvyrzt~}€€€€€€€€€€v}w{vztytyswtwvwvxvyxxzr}j}y€€€€€€€€€~}}{xwo|s|xzvzuzuztyryrzq{q|q}s|s{tytzs{r|q|r{r}s|t|s|r|p|p{qzsyqyqzozpxrvsvtvswqwqwpxrytzt{uxxlzm~{€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~{uˆmŽh’d’baaŒaŒaŽ__‘_“_‘_]]]__^`dŠfŠl‡j‡h‰d‹aŽ^Ž]Ž^ŽaŽaaŽbcedi‰k‚qyxyx|w}ux||€€€€€€€€€€€€~~||tvkxrtvrwrxrys|sprz~~~~~~~~~€~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~~}}{}|}||y{yzz{{|y}xx€xx~y}y~y~y~yy€y‚y‚yƒu…u„z~€€€€€€€€€~}}{{z}x~v~x~}€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€{yzx{y}|~}~|}y|u{uzw||€€€€€€}}|zyxurupwrxownwrxvxzvzwxwvuprkolnmrryu{p|x~~€€€€€€€€€€~y|{|wzuztytyuwuxtyuyxyznzn}|€€€€€€€€€€}}{uuqzt|v|v|v|u{t|r{q|q}r~r~s|tztys{s|q|q}r|r}s|r|s|s|q|q{qyqypzpzo{ozrysyuyszqypxpxqyrxswtvvmyp}|€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~z€uƒnŠhŽeccŽaaŒ`Œ`Ž_^]‘]^^^]Ž]^acŽeŒl‰o‡kŠe`Ž^Ž^`cbccdeŽebˆf€mwqtrvwyx}s{€€€€€€€€€€€€}|uviwqrxqxqxrytzt{u}u}t{yzz{z~uw‚z‚~€~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€}~|}|}{|y{y{{|z}xxx€xxy~z}y|y{x|y}yy€y‚w„x‚|€€€€€€€€~{~x€w€v~w}y|}~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€{zzx{y|{}}}~{}w{u{x|{€€€€€€€~}|{ywuprjqgtdvfzozvyxwxwuwstmplmopovs{v|s}z€€€€€€€€~~{|{zyywyuytytxtytyuxzx{p|v~~€€€€€€€€€€~~}|ypxpztyu{u|u{tzt{szsztzt{t|tztztyszs{szs{t{t|t{szszsyryrzrzrzsysxszr{t|t|u{t{szryqxqxrwvtvsuoys}}€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~zƒrŠkfŽdŽccdbbaa‘_]Ž]Œ]‹^Œ^Ž]ŽZZ[_`‹h‡o†m‡hŠb‹_^_‘acŽaŽbcŒe‹fŠd†f}loqlorlssuxyt{y}€€€€€€€€€€€~~}|vvnwtt{sysztzu{u{t{s{sytwtwtxtztr‚pƒy‚~€~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€}}~|~z}y}y|y|y|x}x~xxx€yyy|x|w}xy~y}z~}~€~€€€€€€€|~xt…s†s…vy}€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€|{{y{x{y|{~~~}}y|w|z~~€€€€€€€~~|zyvsmqlumwnwrxvxyxzwzvyuwrvltmrqpus{xyw}}€€€€€€€€€€{|v}v|uzuztxtxtwtxuyvyzxzs|{€€€€€€€€€€€~~}zyj|o{tvtyt|t|tztytxtxuyuytytytyuyt{s{szsytzt{u|t|uztytyszr{s{sytytzr|r}s|t{uzvxuwtxszt{uttssrxw~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€{s„i‹daŽbŽcd’e’d‘bŽaŽ_^‘]]Ž^Ž]]]\Z[Ž]Œa‹j‡mˆjŠe‹aŒ_^^b‘b‘bcb‰d†d‚h}ntqopqownwstxwtwz|€€€€€€€€€€€~~xvstxyt|t{t{t{t{tzszr{rzsytxswsys}rr‚qƒq…v‚x€~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~}~|~z|y|x|x{xzxzx{y|x|yxx€x€wwxy€{~~~~€€€}~{}wtƒs…t…w‚z~€€€€€€€€€€€€€€€€€€€€€€€€€€€~w€x…|„€€€~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€|y|w|u{u|w|{|{{y{y}|€€€€€€€€~}|yxuqnqsvvwvvxvyvzwyw{w|v}s}mxqrtotpxtvv~}€€€€€€€€€€~wvtxuztytxtwswsys{u|x{yuzs~|€€€€€€€€€€€~}}|wwnzrytvtxtzsztytxtwtxuytyszrzszuzuzt|r|r{tzt{u}t~u~u}u|t{szszrzrzrzq{r|r{s{tytysyq{q}s{ttqsprvw~~€€€€€€€€€€€€€€€€€€€€€€€€€€€}€sƒh†cŠ_‹_ace’f‘eŽdŒc‹aŒ`Ž____^]]Ž\]Ž_Žfj‹igŽdŽbaŽ_^Ž`b‘dec‡hjysutssspwpzpyruwxsv{{€€€€€€€€€€€~~~wusvvxtysyszt{t{u{tztytytztzs{r}q~rr€ss€t€qrƒuƒ{‚~}€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~}~|}z{x|w~xx~x}y|y|y|z}y}x~wwwy}€|~xutƒtƒu€w~{~€€€€€€€€€€€€€€€€€€€€€}€v€w||z€q‡z„€}||€|€~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€{w{t|r{q|t|v{x{vzv|{~~€€€€€€~}|{yxuropuuxuwvxvxwywywyw{x|v{svvryqym|ozu~~€€€€€€€€€€~uzt{vzuytwrxqyr{s}w|zx{o{t~~€€€€€€€€€€~~}|{ysqruuyvxuzt{s|tzuxuwtxuztzs{t{s{t{t{s{r{r{syr{t{t}t~t~s}r|r{r{q{q{ryrzs{s{sysxsxrxp{p}p|svsqvnzw~€€€€€€€€€€€€€€€€€€€€€€€€€€~€vƒjŠc`Œ`Œ`ab‘c‘dfŒgŠg‰fŠdb`__^]^Ž_ŽaeŒh‹gŒfŽbŽ`ŽaŽ`Žaaa`ŽafŠiƒq{qstqttquovqwsvttwxuu{|€€€€€€€€€€€~~~~~xyv{w|t{rysytyuyuyuyuxtxtyrzr{r{s{t{t|t|s}s|s}sq‚o…n„y}‚~€€€€€€€€€€€€€€€€€€€€~|~{}y}w~vwxyyy€yy~xxw€w€w€y€}~~~~{}wt€s‚rt€v€{€€€€€€€€€€€|ww~{~}~|}||}y€vƒy…}‚}~|~z}y~y~|€€€€€€€€€€€€€€€€€€€€€€€€€€€€{u{s{rzszsypyqxownxs|{€€€€€}|{yyvrmqpuwuxvywxywyvxvwxwyvwrrunynzi}i|v€€€€€€€€€€~s|szsysyryryr{r{t|v|yw|n}y€€€€€€€€€€}}|zvpnquuyuyuyuztysxswsxrzs{s|s|tzszszs|r|q|qzqyr{s|r~r~r|rzrzr{q|q{rysxsxtytytysyryqyqyqypzsuvoym|x€€€€€€€€€€€€€€€€€€€€€€€€|€o†eŒa_acŒd‹eŒdŒe‹e‹eŠd‹de’e’c‘aa``ŽbŽfj‹i‹hfŽdŽaŽ_Ž`Ž_Ž_Ž^``Œ`‰dkytstnunspqqmporttvvxzty|}€€€€€€€€€€€~~~~~x}uvr|qzrzr{sztyuytxsyrxqzq{rzrzuyuyuzszs{s{s{r~srpƒm…w‚~~€€€€€€€€€€€€€€}}{|y{w~vvuw‚y‚z‚yxx‚x‚xƒx‚x‚wƒy‚{€~~~~~~~~~{|z|vt€stw~}~€€€}}~}}v}x}{|z|zzyz{x|x~w€v‚s‡{„}‚~€}€z€yy}y{z{}}€€€€€€€€€€€€€€€€€€€€€€€€€€€€zrytyuzvyswmvmvjvhxk{v~€€€€€€~|{zzwtoolsqwtxvzvzuzuxtwuwvxvxpxkwmwjyl|z€€€€€€€€}~w{uxtxsyszsztytyuyvyzr~s~}€€€€€€€€€€€€}~{yuouqxsyszszszsxrxqyqzp|q|q{r{r{r{s{t{rzr{r{r|r}s}r}r{q{ryryszr{r{r{szrzr{r{r}q~p~p|p{qyrzuuxnzm|y€€€€€€€€€€€€€€€€€€€€€€€€€v„i‰cbbcfiŠl‰j‰kˆjˆhˆgŠfhiiŽfŽddfŒjŠn‰kŒgec‘b_^_Ž_^\’_‘ab„gxmttsuqxpwntonnnrswvyxztw}}€€€€€€€€€€€~~~}xywzx|t{qzrzszsytyszsyryrzr{r{rzsytxszs|t}t|t|t|t}s}s}rrnƒvz€~€€}|{{z{x}wuuw‚x‚zz€xw‚xƒzƒz‚z‚{}€~~~}}~~~~~~~~~~~~}}{}y|vt‚tv}{yw€vx~z~{||z|y}w}v}vzvzvzv|vt‚p…p…{ƒ~~~|}{€yx€z~{|}}~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€wtvtwuzwzwzpxkwhwfyh|t~~€€€€€€€~|{yywtqmlmpqqttyuzuyvwuvuxvzt|m|jynyn{q~{€€€€€€€€€€~|{xywxuyu{u{uyuxuxtyww{l}w~€€€€€€€€€€~~||yquh|o|szt|t|tzuxuxtzt{s}s|r{szszs{tztzsyszr{q}q~r}r|r{q{qzrzrzq{p{r|s|s|r|rzryqzp{p|q{twwvwvutuuz{€€€€€€€€€€€€€€€€€€€€€€€€~s‡gŒdŽcbbŽfŒi‹l‹lŠlˆk‡k‡jˆiˆkˆmˆl‹hhŽlŒnoŒmŽhcŽ`Ž____`aaabd‰j|orpqtptowovmtnnmlopuuwxwyy~~€€€€€€€€€€€~~~|zw{vx{u|r{sztytyszs|s{t{szs{t{tztztzt|s|s}t}u}u|u|u|t|t}rrs€s€r€w€y€}€~~~~~}{|z{x}w~vuƒv„wƒxx€xx‚x‚z{€|€~€~}|{~|}|}}~}~}~}|{~xv€t‚tu}x|}y~y{|z{y|w|v}uuu~t|u|u}vuƒs†xƒ|‚~}|~z}yyz}{}|~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€sttpunxtzxyqyjxixiyo{y~~~}{zyvsooosutvvvzu|sztwwuwvwvutqvoxqzo}s~}~vxsyuzvyv{u{vxuwuvsywqyn}{~~}|zwmuk~p{szu{wywxvuuxuzs{r}s}r|r{szrzszrzr{r{q{q|q}r|r{q{q{q{rzr{q|q|r|s|r|r{rwrtstswsxtwwuzuxuruqvy|~€q…f‰dŒdŽceŽhŽkŽmŽn‹n‰nˆl‰jŠi‹j‹kŠm‹oŠoŠo‰p‰o‹kŒgŒb`Œ``bŒa^^_ŽbŠg„m}qtropkrjsmuntnsnpkmisnvtyu}{~}|xvww|t|sztztztzt|t}t~s}r}s|s|t{u|vzuzu{u|t}u}s|r{s{s{t{t|r|s|s{t|t~t€wu‚w‚|~€~€~~~~~~~{}z|x}vu€t‚v‚vwxxƒx‚y‚z€{}~~~~~}~}~|{~|}|}|}}~}~}~}~|~zyv€t~tvyz~x~w~v~v}v|v{v{w{w{w~u€t‚o…x‚}€}}|z€x~x~z~{}{~|~€~€~€€oprlukxrzvxpxjwjylzu}|~~}|{yyuqooutyuzvywyxvvvvwuwwwwxsvsrwrzq}w~}~~tzszvxwwwxvyvxvvvvvsxfyp}}~~||{xsrrrytytyuyuxuxuvsxszrzs|t{szryrxryrzr{r|t}s}r}r|s{tysyryqzr{s}r}r|r{tzszrzrxswtwuwuvuuvuxu|nylxp{{~€r†g‰eŒdŽcŽfŽgŽikŽm‹nŠlŠj‹gŽfg‹n…t‚uqƒn…n…m‡kˆg‰bŠa‹acŒcŠ`‹\Œ]‹`‡gqvvqvounrjtkuqvquosnrmolvqzv}u~{~}~|xyryx{wzuzuytyt{s}s}r|r|r|r|t{txuwuvuwvxvyvzu{t{szsytyszszsysxtxtyt{u}t~ts‚o…tƒy‚~~€~~~~~~}}{{x}w~vu‚vv€v€ww„xƒz‚{€{|~|~}~}~|||||~}}}}}}~~~~~~}}|}z~w~sty~x|x|x}w~w~v|uzvyvyw{t~to„x‚}€~~~~~}|~|€zxwy|~}~~~~z€w~ulvmxp{s{typwkulvnxu{z||~}~}}||{{yywxtrorwu|v|w{w{wzvxvwvwwxxyuvsrwszu}{~€€€€€€€€€€~~wzwzuwutvtvvvxuyvzwwyo{x~~€€€€€€€€€€€~~||zvpsutztxtyszsztysxsxszr{r{rzrzrzqysytys{s|t|s{s{s{t{szr{r{r{r|r}r~r}r{syr{r}s}s|tztztysytwvtxmxkyo}{€€€€€€€€€€€€€€€€€€€€€€€€€€tˆfŒccŽbbccgŒh‹h‹d‹baŽck‹t„uƒq†lŠjŒjkŽieŒcŒbŒbŒccŽba‹a†h}qtxpznxpyqwowqvuutsqrpprotuw{z|x~}€€€€€€€€€€€€€€}||zutrvy{w|v{vyuyt{s}s}r}r|s|s|t{sxtwtxtxuyvywxwxvyt{s|r|r|r{r{r{szszt{s|s~s~ss€r‚m…r„zƒ~€~~~}}||z}x~w€wwuu€vvƒwƒxyz{|~}~}|||||~|~|}}||||}}~~~}~z€w€q„i‰r€v{v|v}v}v|uzuzv{u~u‚p‡t…||}~~}~|}{~z€yxxz|€~~}}|{x}wy€€€€€€€€€€€€€€€€€€€€€€zlznzp{o{oymxowqxtywzvzw{z{{{zzyzxywwsqlrttztyuxuyvyvxwvvuwvvxtsspyu|y~}€€€€€€€€€€€€~zzxzuwttvuvwvzu{v{xz{v}|€€€€€€€€€€€€€~€}}zvuvzxzvzt{s}r|szsysxsyrzq{p|q{rzszs{szs{s{s{s{szszs{s|r}r|s|r|q|q|r{r{s{szs{s|r{tztysxsytxvuunujwn|z€€€€€€€€€€€€€€€€€€€€€€€€€€€y„jŠe‹dŒddŽeŽfjŒjŠg‹dŒbŒemŒw†v„s…nˆjŒgg“h“h’gecŒb‹dŒeŒeˆgƒkzstxqzpzpzqzrxpvpvuttrrrrqtottxzx|v~|€€€€€€€€€€€€€€}}|zuqptu}t~s}t|s{s}s|t|t|t{t{t{s{szsztzt{t|u|u{v{uzt|s|r}p}p}q}q|rzs{s|s}uu~u~t~s~qoƒmˆtƒ}‚~€~~~~}}}}{|z|z}x~wu~w~w~ww~x~y~{~|~}~}}|}|}|}|~}~}~|~|~{}|}}~~}~|}y~zu‚v~w}w}w}w|vzvzu|tpˆq†{ƒ{€|~|~|~|~z~yxx„x…x…y„|‚}}}~{y€t€u|pƒu‚~ƒ~‚€€€€€€€€€€€€€€€€€€€yqzq{n{m{mzlyqxuywyvxsxtyxzxzyzxywxvvrpkputztytytxvwvuwtwuvxwzvvvr{w}|~~€€€€€€€€€€~~|zx{tztxtwuxszs}u{sxuw~}€€€€€€€€€€~~€}|{zo|p~tyuxuytzszryrxrvsvtxtzr{rztytytzsztzuzszszsysxsyr{r|s|s{rzryqysxsxszszr{r{r{r{rzryqxqxsxpuqstt{|€€€€€€€€€€€€€€€€€€€€€€€€€€€€~sƒi‡fŠcŒdŽgŽhiŒi‹dŒbbp‰y…z„sŠmŒkŽhŽgfiŒjl‹j‹i‹hŠhŠhˆm‚szusxpzr{q{o|p{qzoxoxtvqvotpsqoqtvzx|x~}€€€€€€€€€€€€€€~~~}|ywprsu}u~u|s{s{s{t{t{t|t|u|u{tztzsysztyuzv{u{t|t{s{s|r}q~q~r|t{u{t|t}t}u}u|u{t{s}rrr€nƒt…z„|~~~~~~~~~~~~}}|}z~y€x€w€x€x€x€yz}{}||||||||||||}}}~|~|~{~z~{~}~}}}}€s€u|zy{w{w{wzwzv{t~ro„{‚|‚{‚|€{{z~yxwƒx„x„y„z„{}~~|}y{y~w}v|vt‚q…y…}„~‚~€€€€€€€€€€€€ytyrzm{lznznyryvyvyvxuwuxvywywywxvwuuqplrvvzwxwwwuwtwuwwxywzx|xvzq}x~}€€€€€€€€€€~~||{yw{u~t}r|r{rztzzqwnyw~~€€€€€€€€€€~~~}}{uyk|p|r{sztyuxtwsvruqvrxr{q|q{qzsytyuyuyvyuyszsyrxrwrwryrzszrzryqypxqxryszr{r{szsysysxryryrxuvquntrv{}€€€€€€€€€€€€€€€€€€€€€€€€€€€€|~rl†hŠefgŒeŒcŒahv†}|w†nŽifffŒfŒi‹l‹pŠq‰p‰o†m‡l„r€y{zt{q|qzqxnyoyozpxoxqwmwluosrorswxxww}}€€€€€€€€€€€€€€~~~|{xtqrvw|v}v}u{tzszszszs{s|t|t{t{s{q|q{rzsztzs{s|s|t{t{t|t|t{s{t|u}t~ss~s}t|u{u{u}t}s|s{s~p…m‡w„}ƒ}€~~~~~~~~~~}}|}{}{}z~yxx‚yy€|~||||}|}|}|||}|}~}~{~z}z|z|~{|z~{~z||z|y}x|wzw{u}tl†x‚{{~{~z‚zƒy‚xxxx‚x‚y‚xz{}~~xy€ww|wzwzw}u€tƒq†v„w‚~€~€~xrxrxnxlxlxmxszwzwyvxvxwxwywywxvwtvtvrrkssvxwwwvwuvvvxtxuzv{x}wwys|y~€€€€€€€€€€€€~yxuxwzv|v}u{u{szuxh~j}z€€€€€€€€€€}~{|zq|o~r{u{u{t{s{rysxsxtzs{q~p~p}q}r}s|u{uzuztzszsxswsvsxszs{s{s{syryryrzr{s{s|s{tyuxvwuwsxsysztzqznwrv{|€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€|}sm…g‰d‹d‹eŒlŠu†|ƒ€}€y…q‹jfŽeŒe‹ddf‹jˆnˆqˆt†wr|m{l|pywwyuytwruotptotqtownwjwjtmsppqtvwvvu}}€€€€€€€€€€€€€€~|zyrvn|v|w{w{w{u{u|t{uzuzuzuzszs{r|r}r}q|r|r{r{s{s|u{u{u{u{uzsyszr|ss€s€ss~s}u}u~u}t}t}t~r€pƒk‡sƒ|ƒ|~~~~~~}~}~{}{}{}z}yy€y€y€{~}}}~}~}€|}€}}{{z{zy~z~{|~}~|~}~}~{{x~vm…x‚{}€{z}y~x€w‚vv€v~w€w€xzy€y{~|~~~z|z|x}v|vzwywyw{u~tr€s‚p„p…uƒz‚~€~€€€vpvpvovkvivkwqyvzwyvxvwuwuwuxvwtvtvtuqpnrxt|uzvxvxvzuytxtxt{v|xuyq{y~~€€€€€€€€€€€€~~vyuyxxxxxyw{v{u{uyjp~}€€€€€€€€€€~~~|~{{xr|r€s|sztyszszszuzuzt{s|q|q{q|q|r}s}t|s{rzqzrzt{uzszs{s}s|s{tyuxuyuzs}r}r}s}s|s{tztzszszszszs{oznusqzy€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~{}tn„m‡u†yƒ}€~~€{ƒr‹kiŠiˆl‡l‰jhŒl†r~u|v}y~yyqvmwnurtvswswrwqwpvpsotpvpwmvmsmsnrovtzuxt~}€€€€€€€€€€€€€€~~}{zrvlyvyvxuyvzvzvzv{v{u{t|tzszs{r{s|r|r|r{s{t{s{s}t{t{u{t{tzszszr{s}ttusr~r~s}s}t|u|t}r~q}p~n‚rƒzƒ}~~~~~~~~}~{}{}{~{~{zz~z~{~}~}~}}}€|€}€}€}~~}}~}}~~~}|~{~|~}}}~}}|~}}{{z|x}wvvv~u~u~v€w€xz€{~|||z}}}w~w{xv{uzuxwxxzx}v~u}t|u}u}tssƒrƒz~~€vmwoxmxhwfvivpxuywywxvwtvsvtwuwtvsusuqnnryu{uwuuuvvxuxvwvwvxvxxpyo|z€€€€€€€€€€€€~~s|v{xyxxxywzwzw{v|yt|x€€€€€€€€€€}~{}ywvp|r€s~r{rysxtxuyuyszr|q~q}q|r{s{s{t{t{r{q{q{r{s{s|r|q|p|rzsxswtxszt{t|s|q|q}r|r{szsytytytyuytvqvrtsrzz€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~|€}€€€}€w…mŠhŒiˆj†k‡j‹jŽp‰xyxxtwsyx~}~|zrwlvlrnsovpxpyoyoyqwqwqvqvnsmrlqmrmvtzwyx~€€€€€€€€€€€€€€}|zuspwwytxsxuxvyvzv{u|t{t{sysyszsztzs{s|s|u{u{t{t{s{t{u{t{u|t{u{u{u{t|t}t~t}s|s|r{s|u{u{u|s{sztyr|n‚i…zƒ}€~~~~~~~{}{{{€{€||~|~|~}~|}|}|}}~|}~~~~~~~|{{~z~zy€z{~|}~~}}}||y{xzw|v€vuvvuw~x~y}{}||}{}|~u~v{xyyw{v|w{w{x|x}w~w|v{uzuyvzv|v~v€u‚s…y„xkxnymyjxjwmwrwtxuxtxuwtvsurvtvtvsvrupoirswuvuvtvuwuxuwvwwwxwyusxs}{€€€€€€€€€€€€~}xwyywzwyvyvyvywxyw{r~{€€€€€€€€€€{y|wtvm~ps~r}s{ryqxrxryrzrzr|s|s|s{t{t{t{s{r{ryryrzs{s|s|q{qzqxrwsxs{s}s|r{szs{r|r|s{tztyuxuxtxuxuuqvsysxz{€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€}€s…hŠeŒgŠgŠgŠj‹s‡|‚~}zwwqvnyr|x~}}w{qyounuovowpvmuows{y|wztwqsnqnqmrmxr{u|z€€€€€€€€€€€€€€€~}zwprsv{sysxuwuyt{t|t|tzuytytytytytzt{t}t|t{t{u{t{tzt{u{v|v{vzvyuytyszr{r|s}s{szsytzt{t{tztztzs{r}q€nƒk„z‚|}}~~}}{~z{~{}{}|}|~}~}~}~}~|}{}{}{|€|}~}~}~~}}|{{z{z€{€||~}~|}{}y}w}v|u{v~vuƒuƒu‚u~v}x{z{{{~|€}„twzyy{x{w{w{x{x{w{w{v{w{w{vzvzu|v}x|yyyyy|xwixmxpxpxpxsyuyuxtwswsvtursotpurvsvrtpoiqpvuvvvxvwwuwuwuwvxxxxtvwx}}€€€€€€€€€€~~wyvzu{uzvyvzvzuvzn|r}€€€€€€€€€€~}}z|yyvpvq|s~t~t}t|s{r{q|r|szuyvwvyuytyszs{s|s{s{ryqyqzr{s}s}r}q|q|s{s{u|u|t{s{szs{s{t|u{u{uzuytxsys{sypyo{nzw{€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~uh‰cdŽeŽk‹w„}€}zuxpxnxnzr}{}{yxwtrupwowqtqqqst{z}y}vzqvntmsmsowtzv}|€€€€€€€€€€€€€€€€~}}yvnrnvzt{szuyuzu|t{t{tztzsytzu{u{u|t|s|s}s}s}t|uzwyvzv|u}u|u|u{t{syryqzq|r|s|r}s{t{tztysytztzszr{q|r~ql†t„|}~}}{}z~{~{}|~|~||€{€z€z€yxw€xƒz„{ƒz‚{|€}}}|€||~zz€z€z€{{€{€zxvt‚tu~uu‚t†t†s„uwx{zz}}ttw{zy|x|w{v|v|u|u}v|w|x{yzy{xzv|v~v~x|yxyxxyvythuoxuywyuyuyvxuwsvswrwrvptntpuruququqooovtxuxuxuyuwtwuvvvxxwwusww}~€€€€€€€€€€€|~v|uztytxuxvywywxzq}x€€€€€€€€€~}|{yzxvrnuuzuyuyuytytyszs|s|t{u{uzuytzszr{r|r|r{r{r{r{rztzuzt{s{r{r{q{q{r{s{tztzt{s|s~s~t}t|u{u{s{s}r€st{tysrxv€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€}~t€l†l‰t†{‚€€€~|zuxpyoxnynyv}||xwqtmspwpxsuosjtmzx}v|q{lwkukulsowuzv|{€€€€€€€€€€€€€€€€€~~|ztsnru{u{t{u|u|u{uzuzuzt{s{t{u{u}s~sr~s}s|s{uzvzw{v{u{s|s}s~s}t}s|s|r|s{s|t}sst}szszszt{tzuytytyszr|rm„sƒ}€}~}}|||~|~{~zyx‚w‚vu€vx‚zƒzƒzƒyƒ{‚||{~z~zyy€z€y€z€z‚zƒzƒz‚xus‚s‚uu€uuv‚v‚w€x}zy|u€rvzxwzwzw{x{x{w|w~u~u~u|v|x{z{z{z}y~x€w~v{wyxywzv{tjuoxvywyvxvxtwrvovmxjxgvhtismtptptpuqqmssvvuutvuyuzu{uyvyxyyxvsvv}}€€€€€€€€€€€€z|x{uyuwuwvxvxwyyx{u}|€€€€€€€€~}}z|zzxqwp}rs}u{vztytxsxuytzt{s|s|s{szs{s|s}t{tztyszs|t|v{vzu{uzszsyryrzrzr|s{r{r}s}s}s}s}t}u}u|t{s}s€t€t}vyvoyt~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€}~{|€~€€€€€€}|zuyryqzoxmwr{z}|yuvnsotqvrtpqlurzv}q{lwktjujvlsrvyxy{|€€€€€€€€€€€€€€€€}}|zswlwr€su}v}v}v|vzuytzszs{s{t{s|s}s}s|s{s{t{tzt{t|t|t|s|r|r}s{s|s}s}s}s{u|t|t~s}r|s{s{t{u|t|s{tztytzt{t}v€l‰u…}}}~}~}}|~|}z~x€wvw€w€x€y€z€z‚z„y…z„z‚zz~yx€xƒx„zƒy‚z{‚zƒz„{ƒyƒw‚u€t€t~t}v|w}xxy|{y~yv}xx{u|u|v|w{w|w|x~wwv}v}w}x}y}y}zy€xw}v|x}x~w}v}uiuowtxvxvwsvoumukujxkxjugsgritotquqtoqjutxvvtvtvvwxuzu|u|y{yzwuxw}}€€€€€€€€€€€€xzv{v{vzvzv{u{uxzv{w~}€€€€€€€€€€~|}y|yxwkznpƒr~s{tzsytysxsytytytyuyuyuyuzt{s{t{tzs{r|r|r|t{t{tzr{rzrzsyszsys{s|r|r|szszs{s}tttt~tuss|syqtuu}}€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~|}v}s{qznwmtrwy|}|zyququrususutyxxxyszqvptpsqtspws{tzz}€€€€€€€€€€€€€€}}|zurpst~st}u}u}t|t{uytytzs|s|t{szszs{s|s{szs{r{q}r|r|r|r|r|r|s{s|t{u|t|u{u{v{u|s}r|s|uzuzu{u|s|r}q}s~t~u€sƒp…l‰r…{ƒ|~}}~|~{z€zx€v€wxy€yyz€zz‚z„y‚yy€yxxyxyz|‚|‚|‚|‚|‚zƒxƒxxxx~x~xxz|||xx|{x}u}u|u}ww~v~u}v~u~vv~w~x~y~y~xyyxx}w}w}ww~v~tmupwtxtvptjrjqnrnppvqwnvftdshtmuquqsnmirsvvvuvtvuvvvwuyuywzwzuuyx~~€€€€€€€€€€€€~rzp}s~t}t|t{tywvyq|y~€€€€€€€€€€~}z|x{xtvf}ll€o}qzrxrxsysysytytytyuzt{t{t{s|s|r}s}s|s{s|r|rzryryrzr{q{szs{r{s{r{szsysxswsxr{r~r€s€rrst~r|pzozqz}~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~{|w|szqxnuoquqzw~~|wvrtquqvsvuxyzv{vxyxuurtrsqtmwo|pzz}€€€€€€€€€€€€€€~~|zttqwx}u~t~s~s~r}r{tztzv{u|v|vzuytzt{s|s}r}q}r}q}r}r}q}q~q~r~t}u}t|t|t{v{v|v|t|s}r|r{tyuxuxuzs}s~r~rrs€t€t~s€oƒu„{„~€~{€z‚yƒy‚y€xx~y€yy‚zƒz‚zzz€yyyƒy„yƒx‚y€y€z€|€{€|€}||€y‚x‚x€wxx€x€x~{z~zy~zz{v}u~t~t~u~wvv}u}u~u~uvv~x|x|w~ww~x}x~x~w~v~v~v~w~tnuqwtvpugvduitnvmtjxlzkvgthsjsmtotqtnolquuxvxuxvxuxvwvwvxvyvzuu{x€€€€€€€€€€€€~}nzn}r}t{tzuzuzwtwq}{€€€€€€€€}~y|x{xrvl|oo{qyrxtxtytytyszszszszr{r|s|s{tztzu{s{r|r|r|s|r|r|r}s|t{sysysyszsztytxtxryqzpzp{p}p~r~s|r{ryr{q|p}m~o{{}€€€€€€€€€€€€€€€€€€€€€€€€€€|~{~€€€€€€€€€€€€€€€€€€€€€€€€€€}z{t{qzqwptoqrqwtz|}yyvwswsvtvttxvzuzu{wvussrrqtmwq{oy{}€€€€€€€€€€€€€€€}}}zuyo}u‚t€t}t|t~sr}s{szt|u}v|v{v{u|u}t}t}s|q|r|r|s}s~r}r~s~uut~r}r|r|s|s}r}rr~t}u{vztytytzt}t}s}s}r~r~st}t|r}pƒx‚|…{‚{€zyx€xzz~zzz€{€z€z€z{~{~{zyyy‚yzz|~}~}}}~}{€z€y€x}x~x€x~zy~v€u~yy{vzu|u}u}u|v|w|v}v}u|u|v}wxx~x|w|v}v}v}x|w|x}w~w~w~xx€tjtnuqtlxd{fyjuoumsjtkulsnsnsmsltnupuoqlruuxwwwuvuvvvvuxvzw{x{vu{y~}qxv|u|u|u{vzx{vvxv~}~{x~x{wtssyr{q|r|r}r}t|uzuyuytzszr|r|r}r}s|r{sztzsysyryrys{s}r}r~r}s|s{sysysytztztytzr|p~op~q|q|r|r{qzqzr{r|s}r|qxzz~~~|}x{}~}z{s{pzqxpuostrwsyy|}{}wywuyt{txwuyt{r{txrtsstsvozr|qz|~~}|zrzkt„s‚s~t|t}u}t|s{r{r|t}v|wzwzv|u}t}s{szryrzr{t}t}t~t~t~t~u}t|s|r{ryryr{r~ssu~v}v{uztzt{s~s€r€rr}r|r}s}s|s|r€p…t‡y‡{{|y}y~yz€{zz|||~|}}}|~|~|~|~|~|~{~z}{~|~|~}~~~~~~~~~~~|zz~yz~}}€v€u}zz{zzxzw{w{w{wzxzwzwzv{u{u{v|w}x~x~x}v}w}w~w|x{wzx{x|x~x€x€yshtltnuiwexgujpmpnqorprqtstssqupupvqtoplrvuyvwvuwtwtvwvzv|u|wzvv|{~}uzy{xzvzvzwzzzzx}z~~~yv}wytrvtxsyr{r~s~s}t|t{tytxtytyv{t}s}r}r|sztztztzszryqytxtxtxsxryszt{tztysyr{s}s|s|r}q}q}s|t{t{s}r~r~r}s}r}r}vzwuzw~}~~x~yzx~}}y{szq{uzsxqvuuxwxyz~}|xyttssxrytwusxrzpynxptrrtowtzvz~~~~{zoyfr…sƒr~t{t{u{uyvyu{t|t|u{vzvzu{s{s|r}s}r|r|s|t|u|u}u~u~u}t|t|r|r{sytyt{t|s}s}s}s{szsysxsyr{qr€rs}r{sztzuzv{u}t€p†vˆ{†{€z~z}z}z{~z~{}|}|}}~~}}}}~}~}}~}~|}||{|{}|~}~}~~~~~~~}}}||}|}{||y|{~u~x{zyzxyzxzzy{x|w}v~w}x{xzxzwzv|u~w~xx}y{yzy|y}y}x|wzvzx|x|y~yz€phsmvmwiwhulunuotmuovqurvuvuupvoupuqtoqnryu|v{vxvwwuwvvwvyu{w{uy||€€€€€€€€€€~}t}v{xzwzvzxyzvzu~|€€€€€€€€€|~x~v|vwtpytztzr{r{szuxuxuxtxuvuuvwvyt{t|s|sztxuwuxtzszszsysxtwuxvyuyt{t{szszrzr{s|s{s|r|r|s|t{u|t~s~s~s}t{s{s{t{xxzrxr|{~~~~€€€€€€€€€€€€€€€€€~v~xz|}}~~€€€€€€€€€€€€€€€€€€€€€€€€€€~z|u|t}z|xypynzo{ryu|y|zztwpsur{q|swxszo|m{nwospqutyxz~~€€€€€€€€€€€€€€~~}{twmzsƒq‚r~tztxvwvxvyv{t|t|t}t~t}t|szszs|t}u}u|u|uzv{v{v|u}t|t|s|r|r|r|s{t{u|u}t}s}r}r}r{syryqyq{r}s~s~s}t{tzuzvyv{v|u€s†l‹u†y‚|€{z€|~|~|}}}}}}}}~}~}~~~~~~~~}}}}}|}|}|~}€}€}€}~|}||}|~}~}~€xƒruyxwzvzvywzw|w}v~v~vv~x~y|y|y{x|x~x~x~x}y{yzyzyzx{x{v{v}vwyxysoupxmykzozvzxyvvsvrwrvpvotntltlsnsnsmpmryv|x{x{wzvxuwuxuxwzyzxx|}€€€€€€€€€€}~xzxywyvxwxyt~m|r~}€€€€€€€€€€z~v~vztssqysxtxtxtwtvvvtwuytxvwvvwvvwtwtytytytysyszqzq{q}r}s{tztzu{szs{rzrzr{r{r|t|u{u{t{s|r|s|r}r}r}r}t{szszs|s}s}v|szwz{{}{~|€€€€€€€€€€€€€€€€xƒr}xx{x{~}€€€€€€€€€€€€€€€€€€€€€€€€€€{zzw{x~|~}yrvkxkxmtmws|{|yzrvstwsyruxszozlwlvkslsqwwz|€€€€€€€€€€€€€€€~~|yqyqy€qq|sytxtxuxuxuxtyuyuyu{t|t{tzuzuyv{v{w{w{vyvyvyuzt{t|t}s}r|r|r|s|t|u~u~t~t}s|s|s|s{tztxtys{t}ts~s|t{tyvwvxvzv}u~t€ssƒq†vƒy…z|}|}|}|~|}{~|~|~|}~}~|~{}{}}}}}}~~~€}~}||}~yxyw|y~z~y|zyzwzwzvzvzv|w}x~v~v~vw€xxx~w}v|v}w}x|x|y|x|w{wzx|x|x|w~vw€w€vu‚vsxtypzlyqzx|zzxwrwpyn{lymvmtlslrmrnrnpktuxyxzx{v{vzvxvxvywzxyxw}}€€€€€€€€€€€€|{yvxvvytyvwzr|k|v~€€€€€€€€€~}x}t|uwqpuqzrxsxrxswtxtxszrzrzszuyuyvyuytzszt|t{szryqzq|pr€t~u{uzuyszszszszs{s{r|t|uzvytztzt{r{r{q{q{r|r{rztzs|r}p€s€p€o|tyxw|z€~~€~~~€€€„t‚q|uwzu{xy~}€€€€€€€€€€€€€€€€€€€€€€€€€€€~{ywvxuyx}||xymxkvnrnrsz{~|zvtsptrxntttvruntnsopnspxt|{€€€€€€€€€€€€€€~~|zr~o|sr{rysyszsytxtvtututvtxtzt{t|v|u|u{u{t|t{tzsytyuzu{t{s}s}r~s~s}t}t{t{s|s{tztytzt|t|t{tztzs{s|s}s}s|t{tzuyu{u{u|t|t}t}t}s€qƒs…x„y{}|~{{{|}|{||~~~}~|~||ywxvzzy{y|z|z|z|z~x~x|y{y{y|w|v|v|w{w{vyvzv{v|u|v|u~vvwvus~s}u|xzyzx{w{v{vzx{x|x}x~w}w~v~vu€vqwrvmuitlvsywwsulvjxh{i|k{nxquqtptptprlttxxwwwxvxwxwxxxxyxywwwu}}€€€€€€€€€€€€{zwywxuzs}q|wxys}{€€€€€€€€~|}w{tyssmpvr|rzrzpyqxsytzszs{tzs{s{s{sztytxuxuxuytysyszs|s~su}u|u|t{s{s{s|r}r|ryrxswuwuytzt{s{r{s{s{rzrzrzr{s{t|s|p€qo‚n}nvusyx}}~}}t}u|tzvxs{t}w{vyvvwsyrztz|~€€€€€€€€€€€€€€€€€€€€€€€€€€€€~{xwsxrwr{v}{}tzoumrpqvw{|}yzrwlzn|jvptrststsrsprstyv||€€€€€€€€€€€€€€~~}|us|tszsytzt{t{tzszsyszs{t{u|u~utt~t}s}t}t|t{sztzt|u|t|t~t}s}s}s|s|s{t{tzsztytytzt|t}u|t|tztzt{t|t}u~u~u}u|u|u}u}t|s}s|s|t}t€r„oˆv…{„yƒy{}}~~~~{~wy}{}}{}z|y{yzv{t|s~t~v~v~ww~x}x{x{x{w|w}w~w~x}xzwzuzt{t{uzu|v}v}w|v|t|r}s}u}w}x}w}w|v{wyxyyyyzx{x{w|w{w{v|vovoumujtlvnwowktgtetevfxixnxqvqvpvqvqsmsuwxwvxsxtxwwywzwyxxyt{s~|€€€€€€€€€€€€~~xwvzvzvzt}s~xuzu~}€€€€€€€€€~~}z}u{tysqopus{s{szsytytytztztzuztzszsyryrxsxuxuxuzs|t|t}t|t}t}v|u|t|t|t|t}s}r~r|szrxrwtxuyvzt{s|r}r|t|s|s|r|s|t}s}qn‚o€o€q}nttpxt||{yquvzvzvxvyvxvyuxvwvvuwuywr}y€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~}x{r|o}m~o~x~z|pwktmtsxw{z|{vwpzqznvqrtrurtsrsqtrvxx||€€€€€€€€€€€€€€~~~€~~}us{~s|sxtwsys|s|t|t|t{t{t{t|t}s}s~t}s|s{sytxtxtytzs|r}s}t~s}s|r{rzrzr|s|t|v{vzuzu{u}v~vv~u}u|t{uzt|u~v~w~x~v}v~v~v~v}u|t|s|t|t}u€v‚t„u„zz~{w|v}y|z{{z|x|x|w|w{w{v{u{t|u}u|v|v|w}w~x|x{wzv{v~vw~w}x|x|w}v|u{w{xzx|x|x{vyuxtxszu|v}x~x~w~ww~x|xzyzyyyzxzxzxzyzxzxrzt{o{hzhwjujuetbtardshslupvrvqvpvqwqtkvqwvvuwtvvvyvzv{v{w{xw{w~€€€€€€€€€€€€~ywxyxzwzx{wzyn{v~€€€€€€€€~}|x{tztwrpvr|s}s{tzsxsyszt{t|u{u{tztxtwtututwuytzt|s|s~s~s~t}t~t}t|u{uzuzvztyuztzt{r{s{t|v{u{s{s{r|r|s{r{s{s|t|u}s}q}q}r|r|t{mvstws{zzwrvt{tzsztzs{s{sztytxtyw{zs~y€€€€€€€€€€€€€€€€€€€€€€€€€€€€~}w{q{m}i|k{r|z|vzoynyqxryv|{xxtwtxrvtruqupupsrptpwtw{|€€€€€€€€€€€€€€€~~~w{wz€t}sytwtyt{s|t|v|u{uzt{s{r{r{s{t{u{tytxswtxtzt{t|q}qq€q~q|rysxuxtyt{t{tzszs{s|t}vvuu}s}s|t|t}u~u}u~u}u|u|u{u|v}v}u}t|t{t{t{t|t~u€u€w~x{xzxywzwzx{w|v}u}u|t|t}s|r~t~t|u{vzv{u}u}u|t{t{u}v~w~w}w}v~v~v~w}x~y~xxv~u|uzuyvywzx{x{w}vv€w€x~x{xzwywzw|x}y~zz~}{|z|w|m{fwfugtgterdqgsmuqvrwswrvrvrvqrksruxvyuztyuyvzvyvywxwrzu~~€€€€€€€€€€€~}uxuyxyxxyv|q|n}z€€€€€€€€~~}{{tzqysuur|tt~t{tzsysyszu{u{uztzs{t{tztwtwtxuzuzsytys{t}t~s~s}s|s{t{szsztztzs{s|r}s}t}v|v|t|r{rzrysysyszszsysytytyszszs{r{s}kypxttzyw{n~r}sztytzt}t|tztwtvtxv|w{}}€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~}{|v{p}kl}n}v}{~w{twsurusxw{wytwtturtttututstpupvsv{|€€€€€€€€€€€€€€~~~~~~~}|u{s{}x}v{v{u{tzs{tyuyuyuzt|t|t{tzu{u|v{u{t{t{t{u|u{u|s{s{s{szszswuxuxtztztztysyrzr{r{t{u|u|u{tytytzs{s|s{s{s{t{uzvxuxuzu|u}u}tztytxuzt|t€v€wv|u|u{v{xzxzx{x|v|v|u}u~t|s|t{t{t{uztytztzt|t|u{v|v|v|u|v{v{w{x{x|y~yw€w€vu~v}v|x{x{y{xzw{w}xyy}x{w{w{x|x|x~x~y€y~~~}|{{xyowivkumujtitltpvrvswsvrvrurtpplruwzxzxzwzvzuzvxwvxsxlys~~€€€€€€€€€€~~|tvtwvywxxu~m|s~}€€€€€€€€€€~~}y{qzpwrqvq|tu}vzvxtxtytztzuztytzs|t|sztxsxsyszryrwsyt{t|t}s|r{r{r{r|r{txtxtyt{t|s}s~s~s}s{r|s|r}r|tztyszq{p|pzryqxqzozp{q}r€m}o|s{y|t}kp~q{rzszs|s|r{szqyqzs}sz}~€€€€€€€€€€€€€€€€€€€€€€€€€€€~~}y{u|o~lno€t{~}zxttoqpntpzx{uyquqstsuvuxsuqpqpurx|}€€€€€€€€€€€€€€~~~~~~~~~|zuyp|zy|v{v{u{t{s{tysxtxuyu|v}v~v|v{v{v{u|t|s|s|s|t}t}u}t{uztytzs|q}r|r{sztyuyuzuzuzuzvxvxvyvyvzvzt|t}s|tztytzt|u{vyvxvzv|v|v|t|s|s|s{t|u~v~w~v|v{u{u|v|w|w|w|w|w|x|w~v|u{u{u{v|v|vzuzuzu{v}w~w~w~v}u|v|x|x{xzyzy|y~w~w}v}v}v~x~x}y{xxwwvxvzw|x}x|x{x|w}w}w}w~ww€w€~||y{xytxnwlulshsgrjsnurvswtwrvrustpolptvxwwxwwxvxuyuxuwvvvpxv~~€€€€€€€€€€}|qurytzuxxsyl{w€€€€€€€€€|~w|q{quqktozt{s}s{sysxswsxszs{s|s|s|t{sztytyszszrzrzszs{u|u|uzsyrzq{q|p|qzqzs{r|r|s|s|r}r}s|s|t|r}s}t|t|s}p~oo~q}p{ozo{o{q}ris€t~y|tynzq{qzqzrzq{q{q{q|q|q|s}yz}~€€€€€€€€€€€€€€€€€€€€€€€€€€||wzs|o~l‚kor~y~~yyrsnnpksmyv{xyuxswuvtwrwptnpqpwtz}~€€€€€€€€€€€€€€~~}zw{t}|x}u{uzuzt|s{s{r{s{u|v}v~ww}w{vzvzu{s{r|q}q|s|s|t|s}s}r}r}r}r}r}r|s{tzuzvzx{w{w|uztztyvyv{v}t}t~t}v|v|v}u~u}u{u{t|t}t}u}t}s}s~s}t}u|v|v|w{w{v}t}tuv~u}u|u{v|w~w}v}v|v|x{y{xyxywyvzx|wv€u€uv~v~x~x~x}w}w}w}w{wzwzw{w|w|x{xzwzvzvzvzw{wzwywxxyx{x|x|w|w~vv€~}y|tztysxnwjuhtfrdqisourvswswtwtwuwspmntsxuwwvwvwwvxuxtxuyttww~~€€€€€€€€€€||rxuzwyxvxmxm}z€€€€€€€€z~s{pzsssmxpzvzuztytwtvsvsxr{s|s|t|s{tztztzuzuzuzt{t|t}t~t}u|tzsysyszs{s|s|q|q~q~o~q~r|r{tztztztzr{r|r}s}s}r~rr~r}s{rzrzqzszs{o}t~s{zztxnzszryryrzrzrzszs{sztzv|}}~€€€€€€€€€€€€€€€€€€€€€€€€€€~{|u|roj‚hm|pzu}|}|xvrrqqtswuzzzywuvvutuqunrmpqpwvz~~€€€€€€€€€€€€€€~~y|x}zu‚t}t|t}t|s{s{s|s}t}u~uuu~u|t{t{s{s{s{s|r|r|r{r|t|s|s|s|s{s|s|s}s|u{u{u{u|u|u|t|t|tztzu{u{t|t|t}u~uv~v~v}u{s|s|s~s}t|u{v{u|u}u|uzvzvzvzv|v}u~vuttt~u}v}w~w}w|w|v|v|x{xzwzwzw{x|x}w~v|w|w{x}y}x~wv~v}w|wzxzxzxzx|w{w{w{w|v{u|v|v}w|w{v{wzxzy{y{x|wwx€~z|tzpyqxpwixfwfvetesjupwtxtxtwsxtxvxtrnruvxwxxyxxxwvvuwtxuxusxv~~€€€€€€€€€€}|sywzxxxwzq{t~}€€€€€€€€€~}~w|q|qzsrqqvuyvzuytxuwvxvyu{tztytytyrzs{t{uyuytytyt{t|s}t}t}u|uzszrzr|s{uzuzt{s|q}pqq}r{tztztztzr{r}r}r{s{s}sst~t}s}s|r{sysxmws{twzztvqxtzszrzrzrytzt{t{t{vzy{~~€€€€€€€€€€€€€€€€€€€€€€€€€€}z|t}q~mj}l|s{sxszw}}}{xtuqusvsyw{zwvtttssptosorprvx{€€€€€€€€€€€€€€~~}zy|x|x€v{v|t~t}t{szr{r}s~tuu~t}t|tzs{s|s|s}s|s|r|r}r}t}t|tztytztzu{t{t{vyvyuzu}s}t|t{szsztzt{tzu{u|u|u}v}v|v|u{s|s}t}s}t}u|u{u{u{u|t{uzuxvxvyw{w}v~vtt€t€u€vxx}w{w{w{v|v}v~v~u|u{v|w}v}w{xzyzx{x}x}x}w~v~v~w}xzxzxzx{w}v|v|w|w{vyvzv|v~ww~v|xzxyyzx{x}xx€y|p{mylxqwkvgxfuivjvivlvqwtwsvsvswtyuxssopxuzvzwzwzvxvvvuwuxuxnzv~€€€€€€€€€€}}p}r}wxzt{o}x€€€€€€€€~||u|p}qyqrqtuuytzswtvuwuxvytzsysysytyr{s}t}uztyszsytytwuwuxvyvzvztyryqypyryrytytztzt{s{r{r{sztxtxsyr|q}r}szszryr{s{s|s~r~r}q{szpyixrzuwzztpsqtxtysysysysyuyuzvzx{|z~€€€€€€€€€€€€€€€€€€€€€€€€€€}z{t{pzl{gzlytywtwtxz{~}{uyqxsvtvvzzzwyqwpvqtrtotlwn|y€€€€€€€€€€€€€€~~~}zv|s}{~yzv|u}u}u|tzt{t|t}u~u~u}t|t{tzszsztzszs{s{s|t}t~t~t}s{tztyuyuytxtxtwuwuwuyt{tztysysyszs}t~utu~u}t}t}t}t}t~uuv}v{vzv{u|t{t{tztzuzuyuzv{w|v}utt~u~u~v~v}w|vzuzu{v|v}vu~t}u{u{u}u}v{wzwzx|w}x~ww~w~v~v|wzxxxywzv{v{v{v{wyvxvwvzv~uu~u|vzwyxzx|w~xyzyj{i{kxpviwjwiuhwjxlwnvqwrwswswtxtxuxttoqvuzuyvywxvwvwxxxxywzozv€€€€€€€€€€}}q}s}xx}p|p~{€€€€€€€€}z|q{lzovmpppvswtxsxsxuxvwuwsys{s|t|u|t}u{uyvwvxtzrzryrwtuuuvwvyuzt{tytxsxqyqzszsztyuwuwtwsysyrxsys{s}r}s|szsysxsysyr{szq{rzrysynwkytzxx|{urrqtvtvswsxsxtxvyu{u|w{|w~|€€€€€€€€€€€€€€€€€€€€€€€€€€€€z}t|p}q~i‚f~o|wxwtwwx||~{~q{nwmtmxv|y{rxruqtrspukzj}x€€€€€€€€€€€€€€€€~~}ywtuy}y~yzxzvztzt|t|t}v{v|v|v}u}u|u|u|u{uztxsys{s}t}u~v}v|v|u{v{u{uzuztyswsvttutuuuwtyuytytzr{r}r}t}t}t|t|t|t{u{v|vv€u€u}tzuyuzuzuyuyu{u{v{v{v{v}v}u}t}t}t}u|u|t}t}t}t|t|t}u~v~v~v|wzvzuzu|v|v{w{w|x}x~x}x}x|x|v|w|w{xzwzw{v|v|w{wzxxxyxyw|vuu}v|v|v|w|x|x{x{x{y|xlzkznyqxlxjyhwjyjwmuovqvrwsxtxuxuxuxsuluqwvwuxtxsxtvvvwxw{u|p}x€€€€€€€€€€|}qs~ywm~q}€€€€€€€€€~}x{ozkyotlpsowswtwszsyuwvvuwtyrzs}t}t~u|u{vzuyuytzryrypyqyryszszt{s|s{szq{p}q}q}s{tzvxwwvwtyszs{s{s|s}s|sysxtytztztztzrzrzr{s{uzqwpxvz||}}vwsyuyuxuxtxtwsxtzt{t{uzxs}{€€€€€€€€€€€€€€€€€€€€€€€€€€€€~z}s~rx€t‚k€o~r}n{l|o|w~|}uzovktjvq{y{szovmtntqupyp|z€€€€€€€€€€€€€€€~~zyswy|w|wzxzwyuyt{s|s~v}v}v|v{uzuzt|u}v}vzvzuzs|s~s}t|uzvyvzw}v}u}v}v}u|tyuwwvwvwwwxwywzv{s|r|r|r{s{s{szs{szuzvyw{v~utu|tzuzuyvyvyvyuzt|t|u{u{u{u}u}t}t}s|t}t|s|s|s{s|s{s|t|u{vzwxwxwyvzu|u|v{w|w{w{w|w|x|y{x}w}v}v}w|x|x}x}x}x}x|xzy{y{x|w}v}v}v{wzwzx{x{x{x|y|z|ytxqyqyrypwovpwqwovnupvswtwtwtvsvswtvsrkrovtxsxrxsuususvwtzs{n|x||r}szxt|i|t~~|}v|p{ozptorvqwsxuxuztxtvtvsvrxryrzs{s|u|t}t}s}r}r|s{szsyszrzr{r|s|s|s|s{r|r|s|t|s}t}s{tytytytysysys{s{sztytytysytyuyuysyqzq|r~vk{oxwz{{}|wuvwwyvyvxuxsxrzs|t}v}w{xq|y{zyt{s}z~|to€k~l~j}lzp|z|yytvowowpyv{xzrwmtlvsxuxu|}~~~~~{xwr}w{yx{x|x{vzu{t|s}u}u|v{vzuytyszt{u{v{vzu{tzt{t{s|t|v|v}u~t~t}u}v|u|t{tzuyvxwxwxwyvzu{t{tzt{t{s{t{s{r~rtuvu~u}u{u{t|s|t|uzuyvxtzs{s{t{v{v}u~u~t}t|u{v{v{u{t|t{t{s{s{szuxwwvwwxwxvzu{u}v}w}v{u|u|v}w}y}y~xwv~w~x}x}x}w}w|w{wzw{x{w{w}w}v}u|v{w{x{x{x{x|y{{{}||z|w{u{tzw{z{yxuwrwrxtxuyuwtvsvsvsurrksowuxuxvvwtvrssqvqyqxp|zzxuwuxvtvo|y~{{u{rzrvqpwr|syuwuxtyszs{sysytwuvvvvxvyv{t|s}s~r~r~s~s|s{szsxsxsyszr|r|s{s{t{u{u{szsyryryrysxtvswszr{r|s|s|r}q}qzryuxuzszq|r}rug}m|v{yz|{usutvywxvwuwsxszs|u~v~x{yr}{{zyuyr|w~|{p‚j}ozownvnxt|{{wzrzrwrwv{{{vxoumvsxxxz|~~~~{vyn}t|wzxx{w|v|u|u|t}t}t}u{uzuzuzszs{t{t|t}u|tztzs{s}u~w~vvt}t|u|u}u}t|tztytxtzt{t}s}s}s|t{t{t{t{t{s}r~r€s€uvw~x}w{u{s{r|s|t{tzuxuyt{s{u}u~vvu}u|v{vyvzwzv{v|v{u{s{szsytxuwuyu{v|v|v|v}x~x~w}v}v}v}w}x|x|x}x}x}x}x~x~xx}w{w{w{v{v}v}v~v~v}v}v}w{w{wzw{x{x|x}~~}}y{tzrzw|{|zxvvrwrxuxtwsvrvsvsvruqqkpruxvyvzuysxsvutxtzsyr}{€€€€€€€€€€€€|yyxywysys}|€€€€€€€€€€|{ywwuvtptlzn}q{sxuxtxs{s}s}s|szsxtxuxuyv{u|t{s{rzs{s|s{tzsxsxswswtxsyszrzszu{u|tzsxrwqyr{r|r{s{s{r|r|r|s{r|q}p}q{sysyszs{r|s~t~v~k{n|u}w{{ytrsuuzvyvxvxsxsysytzuzwz{x}~€€€€€€€€€€€€€€€€€€€€€€€€€€€€}{{wyszuyv}{~v€l|pvquptpvryx|zzxyyv{v|||}{zvxqvqxuyw}}€€€€€€€€€€€€€€}~}{wtmxuwyuzx{x|w|v|v|v}t}s~t|t{t|s|s}s}s|s}s~t}s|s{s|s~u}v~v~v~u|u{v{u}u}v{uyuxsys{s}ssr~s}s|t|s|s}s}s}s|s|s|u|v|x{x{xzv{t{t}s}s~s}s|t|t|s|u|u|v|v|v{uzuztytztzu{u{t{t{s{s|t{u{v{v}v~u~u~v~w~x~xv€v€uv~w}w}w|v|w|x|x|y|y|x}x}v}v}w|v|v|v|u}v}v~vx€xw|w{w{x{x|x{~~||vzoymxsyxyvuotmuowswquntmuqururuqnniwp{t{u{t{t{u{tzwwytys}|€€€€€€€€€€€€~zywywxwrzv~~€€€€€€€€€~y|tytvvnwnzqysyuyvxvwwwvyvzt{szrysxsxtxuzu{szrzrzsztztztytxswszrzr{s{tyuytytzt{uyuwuwtyt|s~s}s}s|r{r{rztztzt{s{r{rzryrxrxsxuyuzu|l{n{w|vz{zvssuuytytxvxuyszszszuxxx|z}~€€€€€€€€€€€€€€€€€€€€€€€€€€€€}zzwwuutrrvv}||z{wusspsptsuw|{}|zwyuxwzz}}|{yuxpzo{r~|€€€€€€€€€€€€€€}~}|yupwxw{wzyyx{v}v}u}u}t}s}s|t|t|s|s|sztytzs{s|s|t{t{t|t|t}t}u}t}t|u{u{vzvyvyvyt{t|t{u|u|u}u}v|u{t|s|s}s}t|u|t|t|u}v|w{wzv{v|v|v}v}t}s}t}t|u|vzv{w{v{u{tzt{s|s}s}t~t~t~t~t}u}x}y}y~xvtt}u}v{x{x|w}v~vwx€xx~v~w}x}y|{{zzy|x}v~v}x|x|x}x{xzwxvzw}www}w|x|x{yyyw€y€u~p{kyjynxqvpulukunwpvnvjsjsnurvsvsopjxq}t|uzuyuyuytzuytvvu~}€€€€€€€€€€€€~vwsxxxwt{z€€€€€€€€~{~t|p{rvtnvqztyuyvxwxvxvyvzvyuxsxsysytxuxtys{r|q}r{s{u{uysytxtwuytzs|r}s|t{uzu{t|s{sysxsyszsztztzsyqxqxqxsyvzw{u|r|pzpxqyrzsztxtwtwnwk~t{ttzyusrttxrzq{r{r{s{r{qzsxxv~{~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€}xzsxqupsmtqzz~~|zvrtpsrtrttzy~|~t|mwqwvz|~}yvupxp|v}€€€€€€€€€€€€€€€~~~~{}s~y}zzxyxwzvzvzvzvyuyuytzt{t{t|s|s{tzuzt{u{v{v{v}u}s|r|r}s~ss~s}t}u|u{u{t|t}u}vzv{w{v|v}v|v{u|t|t}t}v}v|v}v~u~u~u}u|v|w|w}v}v}u}v}v}v{vzvyvzv{v|u}t}t|tzuzt{u~u€u€u€u~v|x{xzw{v}v}v~v~v~v|w{v{v|v|w}w}x~yyyy~x}y}z{y{yzzzy{xzw{w|x|xzxwwuvwvyw|w~w~w~w~w|wyxv{q{n{n{m{kykvkvlumvlwmxnwmujrjrmsouquqqmmttyuxvvuuuuuvvxvyvyux}}€€€€€€€€€€€€}uxuyzvzu~}€€€€€€€€€€~}zzrzoyrtooqsuryrysvtuvwvxvwvvuvtwtzu{vzvyvyt{t}r}s{tytytysztytwuvvwu{t}t|uzvzu{t|t|szrzqzqxswtwtxsyryryryrzt|t|t|s{rzrzs{s|s|szsxqylxp}v~szzzsuowt{r|r}r}r}s|t{s{t{yy~}€€€€€€€€€€€€€€€€€€€€€€€€€€€~|wyrwpvnultpxx}~~}}u|qxrvpuoys}{~{|suosrvw|{zvvpvo{w}€€€€€€€€€€€€€€€}}~}zwp{u|wvwrytzuxuxuyvxvxuyt{s|s|s|s|r|s|u|u{u{vzv|u|t}s|r}s|t}u|t|s}t~t~t|t{s{t|t|u|v|u}t}t}u}u}v|u}t~tuu}t|t{s{s{s{s|u}v}vvu~u~u}u}t|t{tzuzv{u|t}t}u|uzvzu{v{v|w}v}w}v|w{w{v{v{vzw{w|w}w{wzu{uzu{v|u}vv€w€x€yy~x}v|v{xyzyzxxwwwwxwzxzwxvvuvvxxzx}w~u~w}x|w{vzumunwqysynxjvjvmvpxrytzvytwqtmtkukvkukrisoxuytxswuvuuwvyvzxwxv~}€€€€€€€€€€€€~uvvwxs{w~€€€€€€€€€~~{yzszrwuprmuvutxrzsyuwvvvutvtvtvtwuwuzvywxwyvzu{t{szsxryryszs{txtxvxvyw{w{x{wzv{v|u}u~s}s|sztytxtwtwuxuzs{s{r|q{r|t}t|t|t|s}s}r}s{szqzs}t€q}yzuuqtuxuzt|t{t{u{v{v|v}w~|€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~}{txpvnvluluqwxz|~~|~uzryozlzq}x~}{xtvpvqzx{ywrvq{y~€€€€€€€€€€€€€€~~}{ssvtxtpqowt{u{u{v{v{v{v{u}t~t}s}s}q}r~u}v}v{u{t}t}t}u|u}t}u}t}s}s}s}t|u{uztzsysyt{t{t|s|r{r|s}t~u~u|u|t|t{t|s{rzrzr{r|s|u|u{wzvzvzv|u|t|u|u}v}v{vzu{u}u~v|w|v|v|v{wyw{w}v~v~w|xzwzvyvzuzuyvxwwvyvzvzvzwzv{v}vuv‚xx~x}w}w}w|wzwwwwxyx{x}xzxwxwxyx{x{x{v{x{w{x{x{upupvsxuytzrzoypzu|z~}~~}|ztujsfresfsfsfwjzp|p|pzsxuvxuzt~t{ww~}€€€€€€€€€€€€~xyxxwu|z~€€€€€€€€~|}w}s}uyvpupxuvuwuzuzvxwvvttuswtwtwuwuxuytytyuxuxuxuyszs{tzu{u{t{sytwvwwyvzvzwzwzu{t{s|s}u}u}v|uzsxrwswtxsysxrxsxsyu{u|t|q|q|q}r}s}tzswtwuyvsyxysumxvwyvzuytyuyvyvyxyzv}|€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~}ztxqwowlukumwszu}z~~{ywtvovmxqzx}~~wxupuozt}{zvys|{€€€€€€€€€€€€€€€€~}~~~|zt{yy{qxm}t}u{u|u|u|u{u|u~u~u}t|u|s|s|u}v}w}v~v~u}t|s{s|s|s|t{t{t|s}t}u|t{s{r{r{szt{t|s|r{r{s|t|vzuyuyuzu{u|u|u{u{t|t}t}v|vywvwvwxwzw|v|v|u|u{uzuyvzw{x|x{x|v}u~v|xyxzw{v{vzwxwxxywzwzxzxywywyvzvzwywwxwxxxzx}v€vx€y}z|y|x}x}w{wzw{x|y~y~x{wzwywzxzxzxzxzwzv{vzvyvqupurxvyxzvzo{n}u~|€€€€}|sxiuesfrhshsguewhzhzlxrxwxxwxwzwuxv~~€€€€€€€€€€€€zyzuyt}|€€€€€€€€€€~{}v~t}vxuptrvuxuyuzvyvywxvwvyvzuyuyuwuxuytxtytxuyuyuzs|r~s}u{uzs{s{u{vzvyu{t{u{v{u{s{s{r~t~t~u}u|t{rxswtwtxuxuztzu{t|t|s|r{r{q}s}u}u|rzrztytxxz{twoywvztzrzrztyuxvwxw{o}y€€€€€€€€€€€€€€€€€€€€€~}zxxuxswounupwqxpzu}|}|yvsprourwy{~zzwrwmyq|y}z|v~|€€€€€€€€€€€€€€€~~~xyy}xv|u|u{uyuxtys{t|u}u}u~u~t}t}t}s}u}t}u|v|v{u{u{tztztztztzuxuxvxvyuyuztzs{sztytztzsztzuzu{u{vzu{vzu{t|t|u}u~u~uuv~w|v{vyvyuzv}v~w}w|u{uztxtyuzv{v{w{w{u|u}w|w|w|w|w|w|w{v{w|w}x}x|x{wzvyvyu{wzwxxxxxxxx{w}w}v}w{w{x{x{x{x|x}x}y|z}y}y{xzwxxxwxwxvxvzvzvzuxuvvltltowu{yzxypyn|u~~€€€€}|sxjwhvkumrnqlpipgrhtlvrywzuxuyv{m{u~€€€€€€€€€€€}w}p|v~~€€€€€€€€€€~~y|u|uzxtumwpxsxuxuxvyvyxywywyuytztztytytysytxuxvyvzu|t~q}r}tzuytzs|t|u}t{t{szszsztzsztzr|s|s|t|u|u{uytxtyr{s|r}r|r|r|r|s{s|s}s}st€s~q~o~r~tz|{yxpwu|s~p}q|r|tzuyuwwyvv||€€€€€€€€€}|{||~~€€€€€€€€€~|{wztzrzpwqvvvxutvsxu||}{yqvovsu{y}~||szoxoyt{z{y~}€€€€€€€€€€€€€}}}€~~wzwzy~r}u|w|vzuzs{r|s}u}w}w}w|u|t}t}t}u}t}t}u{t{t{t{t{t{t|t|t|t{uyuyvyuzt{szszrzt{t|u{uzuyuyuzu{u{uzwyuzt{s}t}t~uuu~v}u|t|t{u{u{u|t}u}v}u|u|tzsyuyuzu{v|v}v~v~u~u~t}t}u}v}u}t|u}t|u}u|u{uzuzt{u|v|w|x{x{x{x{xyvyvxuyvzv{u{u|uu€v€v~w~xzyxyxxywzv{t{s{t{t{vzvyvwwivkuowv{z{yytxr{w~~€€€€~~}v|ozlymvorpropnpnppoqqvxxzuxtxsymzw~~€€€€€€€€€€~~~wqy~~€€€€€€€€€~}}x|t{uxvnsnxqxrwtwuxuyu{vzu{tzrzr{rztzsxswswuxwxwzv{t}s}r|s{s{tzt{tzt{t}t{t{s{szrzszrztzuyuxtzt|u{u{tzt{t{t{s{qzrzr{s}s}u|u}u}s}r}r}ozpxpys|u{||{ynwr{s}r~s~t|u{vzwyx{wz}~€€€€€€€€€~~}|v{t~|€€€€€€€€€€~~~{}v}t|u{qyrxvxwwtwswt{z~}|xwrupsvt|w||zytwpwqyxz{}~€€€€€€}|~|~~~€||~~~~~~yu{s{{nq{wyxzu{s}s~uv€w€x€wv~v}v}u~u~u~u~u~t}s|s{s{s|s}s~r~q~r}t|u{t|t}r{szt{u|u|u{uzuytytytxuxuyvyu{t|t~t~s~t~t~u}u{uztztzt{t{t{tzuyuzu|t~t}t{tztyt{t|u~v~u~t|s{s{uzw{w{w{u{u{tzu{v|w}v}u|t|u|w|x|z|y{y|x{xzxxxwxxwyu{t|t}uu~u~t~u}v{xyxxx{w~ut~s}t{t{v{v{w|ykxnxuzy|{{z{w{w|{~~€€€€~~{wxqwmxmvotpuprqrqqtnvqxvxyvxuwvwoyw~~€€€€€€€€€€~~~~z€v|€€€€€€€€~~{~v~s{uwsmpptrvtwuwwxvyuzuzt{t{r|t~t~t}t|szsyuyvxuxuxtys{r}q~q|s{tyuyuzt|s{s{tyuxuxtzs{szvyvyvzvzwywxxyx|w|v|u{szszs{t|t|u|t}t|s|r{r{lypxryt{v|~|zrnrus|s}u|vzwxwyvzy|{}~~€€€€€€€€~~}}v}t~|€€€€€€€€€€~~~z}v}w|z{vxrwswtxuyuwwvy{}~|ytwouqrutxz{}|zvwruswyx~}€€€€€~}zzw{w}|~~~~~~~~~~~~~xxwvy{m{gn}uxwxvzu}v~v€v€vvv~v}u|u}v~v~v~u~t}t~t~t~t}t|s|r|r|r{t{t{u|u}u|v{w{v|v|w|uzu{r{r{rytytzuzu{uzv{u{t{s{t|u{vzvyuzt{t{t}t}t|t{v{v|u~t~s~s}t|t|t}u}v|w{vzvywyxzy{y{y{x{wzvywyw{w}w~w}v{xzyzz|z}y|y{yyyyzyzzxzx{w{u|u|v{wzwzv{w|x|x{xyxzw{v|t}u|w{x{yzyzz{xrysyx{{}||||{}|~~€€€€~{wvqwqxpwpvntmqmqoqrrsvtyvywwxvyruxx€€€€€€€€€€|y~€€€€€€€€€€~z~t~s{utthvmzrwvxwxxxvxtxryr{s}uuuu}t|t{uyuxuwuvtvuwtys|r|s{txuwuxuzuztztyuxtwsxrysztyuyuzv{wzwxwvwwtzu|t~u~t}s|rzrysys{r}r}r|s|t}n|r{szuzxy}|}zsmstsyszuyvxvwvwuzx|€~€€€€€€€€~|}w}w~€€€€€€€€€€}{{wzw}||{{uysxsxtysyrwtyw|||x|pzoxmxp{w~|{zutqrruvz|~€€€€}{{v{v|z€~zvvux{krmzq}t{wyvzu|t|t|t{u{u|v{u|t}suuv}v{v{w|v}v|v{v{u{u|t|t{t|u~uu~v|w{w|w~xw~u}t}s}s|s{t{t|u|u|u{vzvzuzu{u{u{vzv{w{w{x{w|v}u}v}v}v|v}u}u|t{tztzt{u}v}w}w|w{xzw{wywzwzxzxywywxvyv{u{v{wzwyx{w|w~v~w~x|zzz{z{x{xzwzx{x{x{xzxyw|x|x|x{xzxzxyvyu{v|w{y{yy{x|xxpysyw{z}|~}~~€€€€€€~|wzqzp{pymwktiqipkolontrvuvwvxvxstyz€€€€€€€€€€€~}€€€€€€€€€€~~y~t}uzvsrnws{vyvxvyvyuxtwrxrzs}t~u~u|uzvzwzwywwvvwuuvvvuyr{r}t{uxwxvyvytyuwtxtxsyszsztztxsytytzuyuwuwuxszt|t|t}t|r|r{ryrys{r~rq~q{sznxqyqysyxv~{}{vtvwvwuxuxuwtwuwvyx~~€€€€€€€€{~x~z~€€€€€€€€€€~{{x{y}}~{~v|uytzrzpynxszw}{{wyswnwoxv{y}{wtroqput{|€€€€|~x|w|z~€€€€€€€~xwptu|e~h€p€rv|x{u{u{u{u{v{v|w{v{v{u~u~u|wzwxwyv{v{v{w{w{v}uts~s}s}t}u}u}v|x|x{x{x|u}t}t|u{uzuzu{u{v{v{vzvzv{u|u|v|w{x{x|x|x{v{u|t|t|t|t|u~v~u}u|u|t|t|t}u~v~v}w|w{w{vzwxwxwxwyw{x{w|w{v{w{v{u{u{t}u~w~x~x}y{xzxzwywwwwxxy{y{y{xzxzxzwzw{xyyxxyvyu{v{u{v{vzxzyzwlwmwryx}|~~~~€€€€€€~}wzowmvntmslsktktlqnmqotruuwuxvwus|z€€€€€€€€€€€€€€€€€€€~w~t}vzvqsrwwyxxvxuxtwuvuvtxtzt{t{t{s|t{u{t{t{tztyuxvvuvuwuwvxvwvwuytztxrxrxrzrzszsztytztzsztytytytysytyuzuzv{u{t{s}r}r|rzuyv{u}sr}s|nyswtwuyx}~~~|wwwxwyuzt{sysxuxvyx{{}€€€€€€€€}{~x{€€€€€€€€€€€~|}z}z|z~}~||yxuxuxsxowrwt{z}}|zxtsusxuzz{{yvqsqurzz€€€~z}x}z~€€€€€€~~yxmqt{g‚do€o€r~w}v|u{u{v{w|w}v}w{x{x|w}x|wzwxuys{s|t{v{w|wuu~t{s{r{sztzvyvyvzvyv{u{t|t|u|u|w{v{v{u|v|v{v{u{t{s}s}u}w{x|x|xzw{u|t|s|t}t}t~uw~w}x}x}w|vzuzu{w|w|x{wzwzwzvzuzvywxxyxzx|w|v|w|w|v|u|u{v{w|w|x|x{xzxzxyxyxzxyx|w|w|w{wywzv|u|wywxvxuzu{u{uzv{w|x|x|wivlwsyx}|}}}|}|~~€€€€€€|wxoumrnqqqpsovpxpwpssqxsztyuzvyuv{{€€€€€€€€€€€€€€€€€€€€€€|w~t|vxrpprvwwwxvyuytyuwwwwwwyvyuzuyuzuytysyrzsytxwwwwuxtxtytytyrytyuxuwuxtxtysysxtwtxtzszsyswswsyszrzrzrzs{t{uztzt{t|t{t{uyuyt{t|s}t~s{tyu{s~t€|~}xsvtuxrzq|q|qztxwwwy|}€€€€€€€€€~~}~€€€€€€€€€€}||{|z|x{w}|}{{v{tyszoxpuqww|}}}wysuwr{w|||vvtrrqxw~~€€€€~}}{}{~~€€€€~}~{xqpvyr}r|v|x{x{v|u}t~u|vzwzw{v}v}w|w|v|v}v{vytys{s|t}u~v~vu~u|t{s{r|s{uyuwuxtxuxtzt{s|s|s{u{v{w|w|v|v|u|t}t}s}s~s}t|u|w|w{xzwzuzu{u|vvu€v~x|x|y|x|v{vyuyu{u{v{vywwvwvxu{s|t{vyxxxzx}v~u|u|v}w~w}w{wzw|x}x}x|w{w{x}y~x~x|w|v|u}u}u|u|u}u|v{vzwzw{w|w|v|v}w}x}x}xoxryw|z}|}}|{|{~~|xwqwmtmsnrospuovovmsqrxs}s{uyxxxt||~~{~w~u{vuppstywyvywzv{u{vzuyvxvywyvzvxvyuxtxsxrwrwswuwvxtzs{r|s|t|s{tzvxvwwwuxtysxrwswsxsxrxtwtvtwtxtxtxrzqzr{rzsxtxtyuzv{u{u{tztytxuyvyvyvzt|qp‚{~~xvswszq|q}s|tyvvyuzq}z}|}{}{|xzryt}|~~{~u~r}m{nuntrxy}}~{|vxsvvxw{y{yxtsqwu}}~~~~~}{}}~x|u{w}th}rzuyu{u}t}r|t|u{u{u|u}v|v|u{u|u}v}u|u{u{u{u{v{t|t}s|s{s{r|r}s{tyuxtxsytyt{t{s}r}r|s|u{v{vzuztzs{s|s|s|s|t|t{t{vzwywzwzvzvywyx|x~xw~u|u|vzwyvzv{u{v|w{w{vzv{uyuyt{s}t~v|xzy{x}w~v}u|v|v}w|wzwywyw{w|x|x|w}www~v}v|v|u}v}w|wzxzw{x|w|w{wxxxxzx{w}x}w}w~wswtxw{z||~~}||{~~|xyrxowkwhugxhxixlvkqonwp{szuzxuyp}z~y~u}u{tuqswu{uzuzuyvyuzuzuyuxsxtzt{u{t{szsyszsxtytytxtxszr|r}t{uzvyvyvxvyuzt{s{r{qzqyrxtytyuyuywxwxvwuxtzr|s}s|tztxuxvzw{u}t|szsxuwwwxuxxw{w|u}u|~~zysxs{r~s~u~v|wzx|wy}|}||{}y{uynvlwt}}~zv€q}pvrssvw{|}~x|pyoxqyt|z{xwqxt~}~~z~|~}wzp{vv€ik|oysyt{s}q~u}u}t~t~v~v}v|u{u|v}u}u}v|w}x|x{wzvzt{u{u|t}s}t{tzvyuytztzuzv|w~uts~s}u|v{wywxvyuztzt{t|t|u}v}x|w{xwwxvzu|u{wzw{x|x}w}u|u{vxwxwyu|u|v|w|x}w~v~v{uxuzu|uvv|w{wzwzxzx{x{w|v|v|w|w}w}w{wzwzv{v}v~w~v|vzwyxzyzyzxxxxvzw{w{w{wzxyxyxzw|x|x{yzuoupvsyw{{}|||||~~€€}|w{qznylyixhxhwivkujqoouryuzxzztzp}{€€€€€€€€€€€€€€€€€€€€€€}xt}txrsptvtzsytyuxvxuyuzuztysyszu{vzuztxsxsxtxuwuxtwtvuwuwuyuztztytytxuyuytzt|s}q}r}s|u}u}t~t}t|u{u{u|t{t{t{u|v|uzvzuzu{u{t{szrysyuzw{v|u|v|w{z{{z~~|zwrxvwzv|w|v|u{wzwv||€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€|z|z|z|wzqvnuoyw}}~€{€u€r}pypxuxy}|~{}qymwmxo{w|zytxu~|€€€€€€€€€€€€€€€€€€€€}€y|~|tvrzw}y|w{t{q{rzt{t}qsu€t€tt~u|vzvzuzvzu{vzv|v~v}v|v{v{t{t}t~s~r}t|t{v{v{vzvywxw{w~u€stu}u|v{vyvxvyvzv{v|u|t{t{v|w}w}w|v{u{t|u}v}w}w}w~v~w}v|wywyvzu{u|v|w|w}w~v}w{wxxzw|v~v~t{tyuxuxvyv{w|v}u}u|v|w|w}w}v{wzw|w~w€v€v|uyvwvxwzw|v{v{w{xzyyxyxzwzvywxwywzxyzwwovnwpxuzy|{|{||~~~~~~~~||x|t|qzpxovltktkvoynxowsxuywzx{s{t~|€€€€€€€€€€€€€€€€€€€€€€€€€|xu~txpusxvwxuzuzvxwwwwuyuztzsytyuzvzvxuwuvtxtyuxtxtwtvtwwxvxuytzszs{tytxuvvvvwuytzt{t{t{s|s{s{t|u|t|u|t{t{u{t|v{vzv{v{uzuztzs{rzrzs{t|s}s|s|s|w|uy|}}{wpytxxwywywzw{yzxv}}€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~|}{|{{xxsunulyq}y~}{x|r}o|nyrwuzw~||xzqxnyp{w|{ywyw~|€€€€€€€€€€€€€€€€€€€~{}v|z|{rxs{u}u~n{uzyyxxwxtzt~u~w~ut~t|t{vzvyvyvyvywyv{v|u}t|u|t|s|s|r|r|r{s{s|t|u{uztzt{u~t€t€s€t~v|w{w{v{t{t{uzvzw{u{tztzvzw|w}v|u{t{u|uu€uu}v}uu~u|v{vzvzu{t|u|v{v|v}v}v|v{v{v}v}v{uyuxtyt{u{w|x|x}u}u}u}v|v}w}v}v}w}w~w}w}w|vzvyvyv{v{v{wzxyywywxxwzvzvyvyvzw{x{x{xmxlvmwryx{z|z|{~}}|{z{z|{|{|z|y{xzwxuuosnwuxuxtwtxvyv{v{o{t~}€€€€€€€€€€€€€€€€€€€€€€€€€€€|ƒwu}uwpwryvxwvyvxvxvwvwuwuxuwtvtvtxuyuyvxwxwyw{wztytxtxtyvzvzwyt{t|s}s|txwuwtvuvvtvtwtxtxsxsxszt{v{u|v|u|t{t{tyuwuvwwxxxyvytzr{s|s{uyvytzs{r|q|t|syy|~|wswuwxuxvxxyyx{w{x~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€{|}z{uvpsnvmyq{u}{~}{~u}o|jylxqxt|{~|}v{rzs{x{|xxzw}|€€€€€€€€€€€€€€€€€€€~|zwzxu{l„u€w}u~nyrxvrxp|w{yx{w|w|w}u|t}t|t|t{t{uyvxwwwwvyuztztztztzs{q|q|r|s{tyuxtxtxtzs|t}t}t}t}t}u}v|w|w|u}u}w{wzw{u{u{vzwzwzvztytyvyw|w~v~v}u{v{t|t}t|t}u|v|u|tztyuyvzw|w}x|v|vzwzwzvyvzvzv{v}w}x|xzwzu|s}s|t|v|x|x|x|x{xzxzx{x|x}x|x{xzwzwzxyxxxxwxwywzwzwyxyxzy{yzyxxlxjwkwlxsyw{x|z}|||{y{y{{||~}}}|{{yzxvrqruvwuwtvsvtvvwwwq{u~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~€{v}u{vtruuvwtwswtxtyu{vzvxuwuwvwvxwxvzvzv{v{v{u{tytxtxuytzuzuyvxvzu|s}s|tzvvvuutvvuvwuvtwuwvwxwyxzxzwzvzuzt{tztytxuwvvvwvxvyu{t{s{sztxswtxuwvxuxuysxz|~zxywywvxvzwzyx|u~z~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~{~|}{{vwrtrtrwszu{x}}~~{}uzkwlxpxtzw}|~{|tzu{x|yys{s||€€€€€€€€€€€€€€€€€€€€~~{|zu|j†u‚y|xzpqoxq{qxus{r~vyyu{t|u|u}t~tt|s|tzuyuxvxuxuytzu{u|u|t|t|t|t}t|t{vztytyt{t|t}t}t|t|t{t{v{u{v|u}u}u|v{w{v{v{v{v{vzu{tzt{v{w|x|w|w|v{v{v|v{u{u{u|v|tzuwuvuwvzw|v~u}t}t|u|u|t}t}u|v{w{x|x|vzu{s|t}t}u|w|w|w|v{uzuzw{x}x~yy}y{xzwzw{x{x|w{v{wzw|v{uyvywyyzzzz{xpxmvkulvowtyw{y|{|{{z{z||}}~~}}{{xxsssvwxvxtxsxtwvvuuqzx~~€€€€€€€€€€€€€€€€€€€€€€€€€€}~w|t|uyuuuuyuzuytytzt|t~v}w{wxwxuzu{v|w|v|v|u{tzsysytxtwvwuyuyvwwwwxwzt|t}t}v{vzvyuxvwwtwswuxwxywzwzvyvxvwuwtyt{t|t|tzuwvuwvvwvyu{tzszr{oztxxvxwwyu|u}{}|z{vzuxxwzv{wz{v|{€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~}{}{}z}w}p{kylxszwzw{y}}~~||v{tzsyryqzw~}}z|x}x{vzn{q}{€€€€€€€€€€€€€€€€€€€€€€€~€~€x‚j€qyyxymum{n}m}qxxml~wz|v|v|v{u}t}t|tzuzu{u{t{t{tzuzuzu{u{u|w|w}w}u}u}v}u|t}t}t~u}u|u|u{t{tzu{tztyt{t|t}v}w}w|v|v{v|u{u{u{v{vzwzvzv{v|w}w~x}x|x{x|w|w}v{wxvyvyv|v~v€t€st|t|u|u~u~u|v{w{x{x{yzxzw|v|w|w{wzv{v}v|u{u{v|v}w}x|x{x{wzwzxzyyxzv|v}w}x|x{xzwyvyv{w~xwsuntltlunwsyx{z|{|{|{}|~}~~}}{ywtqrwxwxxxxvxuxuxqwm{x€€€€€€€€€€€€€€€€€€€€€€€€€~}{{vzsztvqsswwwxwxuytyvxuywyyxyxxxvyuyuyvyvyvxvxuyuzuzvxuwuwtxtwuvwvwxu{t|t~t~v|u{uzuyuxvxwxuyvyuyvxvvvuwtwtvuuyt|u|uzuyvxvwxwwxvyvzvyt{s}o|ryuyvzu|t~v€}}}xyvwvxvyuyxwzt|{€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~}|}|}||zx€r‚nu{y|y{x{y}}{~w}u{s{p{u}|~~{}z{yzv|y~~€€€€€€€€€€€€€€€€€€€€€€€€€€€~€~~y{{z|m{k}m|k}l|p}ozwz}z{zy{v|u}t}t|t|u}v|v|v|u|t}v|v|vzu{u{u{u|u|u|v}u}uuu~u}v{w{w|w|v}v|vzvzuyt{t|u|u}v}v}w}v|u|u|vyvxwxvywzvyvzw{w}w}w|wzw|w}x~w}w{v{v{v|w|vu€u€t|uzuzvzv{w{wzwzwzxzxzyzyzxzvzvywxwxxyw{v}v}u}v}v|v{w|w|w|v|v}v|u{t{t|t|u|wzxzyzyywyvzw|y}yqvjuiuluovszx||}}}||{}|~}~}~}~~}}{zwtprwxxwyxxwxvys|l{j}x€€€€€€€€€€€€€€€€€€€€€€€€€}}z|v|uzvstpxsysyuyuzvywxvvwvwwwwuwuwtvuuwuwuvuvwvzv{vzuyuxtxtwtwtvvwvyu{s|t}u|w{uzt{t{u{vzwzvzvyuyuyuxtxuwuwtxtzszryrysxtzu{v{wzwywzvzt{tzqyrytyvzu}ss|~}|wywxuztyuyxwzs}z€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~}~}}}~}}~{‚{~{}z}y|y|z~}~~|~{}y}x}y}{~~~~~~~}~~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~y|{|}mp~vzryvxxyy|z{{z|w{yz{y|x|w}v}v~w}v{uzu{t}v}w}w}v}v{tztztzt|uu€u€uu}t}u|v{xzyzxzxywxwxvxu{t}t}t}u|v{w|v}u|u|vyvxvxuyv{vyvzv{v{w{wywywzw|x~w}w|v{vzv{v{v{t|s|t|v|v{w{wzxzx{w{w|w|w{w{v{u{u{vzwywywyvzvzu|u|u{vzwyw{w|w|w|w}v|v{u{u{v{vzvywzy{z{x|w|w|x}yvwmvjvkvlvsyx|{}||{|{}}}}}||||{}}}}|{wtntvxxwzvxwwwwuynym|x€€€€€€€€€€€€€€€€€€€€€€€€€}~zv~v|uussxs{r{s{uyvxwvvvvvvwwwwwwuvvwuxvwwvxuyuzwyvxuxtxuxuwuvvvvxvzwzvzwzwzwzvyvyuxuzvzv{vzwxvyvyuyszuyuxtwswrxrxrxtwuxwyxyxyxwwvuvvvvwuxuxvwwwvyu{t}u{~~~}sxtzs{s{tzwxwu{{€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~}~~~~€€€~}~|}x}y}y~{~~~~~|~}~~~~~}~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~}~~~~}}~~|x|uvx‚x†xƒ|€}~}}{|{|y|x{w{w{yzy{zzzz{xywxwwtxuyt|u~u~u}u|s|s|t|v{v{vzvxvwvwwwwywzv{v|v|v|v{w{v|v|u|uyuyuzvzvyvwvxuzv{vzwzw|x}xx~w}u|u{v|v{vztzs|s}v~w}x{xzxyxywzx|x{x{v{vzvyvyv{v|v{vyvyvzv{vzvyuxvxw{w}wwxx}x{xzxzx|x|x{xzxzx|w{x{yz{{{ywqwmxmxoxtyx|z|{|{}}~~~}}|||{{||}||{wvmxtzvyxwxvvwuwsotn{z€€€€€€€€€€€€€€€€€€€€€€€€€}~yv}uysrrsvuwswtxuxuxvwvwwvwvxuwvwwwxuxvytytysytxuwvxwyuyuyuxvwvxvxuxvyvxwwxvwvwwvwvvuwvwvxwxwxvyuzt{t|t|uyuwtwsxrxsxtwwwxxxxxxwvvtvsvtwwvxtxuvvuvwtzt}k~{~~}ryrzs{s{s{tzsyz}€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~€€€€€€€€~}~|~|}{}y~z~|~~€€~}~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~€€€}w~wyz||€z~y|{z|y}y~z|{{zzyzx|y|x|x|w{v{wzv{w{x{wzv{v{u{u|u}u}w}w~w~u}t}t{uzvywxwwwxvyuzu{v|v}w~w}w{u{tztzu|v|v{u{u|u}u~u}v}w{wywxwywzxzx{x{wzwxwxwzvzvxvwwxxzx|x{wyvxuxvyw{x}y~y}y|xzxyxyyzx{x|xzxyx{xzyxxxwz|zztzpypytyw{y|z|{}|~}~}}}~}|{z{||~}|{xwnvs{uyuvwtwtwtunwr}|€€€€€€€€€€€€€€€€€€€€€€€€~|}w}u|wyrspttuuuwvwwxwxwyvxuwwvxvxuvvuxs{t|s{txtxtwuxvzwzwzwywxvyvyuyuyvzvxwxwwvxvxuxvwvwvwwxvxuxvyvzw{v{vzvxvxtzs|r{szuwxvxwwxvytxuwuuuvuxsxsuuuuuuwszs{qzz}~~wwrwsxryrzt|u{|~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~€€€€€€€€~}~~~~~}}|}{}{~}€€€€€~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~|yzx|{~|~}~}}z||{}z~z~z}z{{z{yzyz{y{y|z}z~y|x{vzwzw{xyxzxywzw{vzuyuzu|w|w|xzwyvyuyvywzwzwzw|u}u~u~u|vzvxvxvyv{w|x}x}x|w{wzxzxxxxxwwxxyx{y{xyxyuyvzw|x{yyxxwyv{w|wzxywyxzxzxyxyxxwxvwux}|{wztzvzx{y|{}|~}~~~}||}}~~}|{{||~~}|wumrsxvxvvwtwrxsvnyt~}€€€€€€€€€€€€€€€€€€€€€€€~|w~v}v{ouptttwtztzvzwyxzw{vzwywxvwsxs{s}u|uzuxuyuyv{w|w|v{wzwywyvywxvxwwwvwvwvvxtztzuzvxwxvxuxtxuwuxvzu{uzwxvyu{t|t|t{uzuytzs{s{s{uyvxwvwuvsvrxtwvwwvxvxtx{}~~yxtwswryszx{|{~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~~~~~€€€€€€~~~~~~~~~}~|}{~}~~€~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~}}x}w~z~}~}~{~u~s}x{{{}z|z|z{yzyxyxxwxxxyyxywzwyvxvxwwxwyv{v{v}v|w|w{vzuyuyvywzxzxyyyxzx{x{xzyyyxxxxxwxwyx{y|y}y|x}w~v~v|vzwyw{w}y}y{xxxvxvyxyyyzxzwzvzvy}|{yzxzx{z{z|{||}}~~~}zzyz||{|{|}}~~}|wultsyv{vzuxuwvvupyu~~€€€€€€€€€€€€€€€€€€€€€€€€€~~z€vu|uvoqur|s}s}t|u{u|v|w{vywvvvvwt{s}s|t{vxuvtwtyvzw{w{vzvxwuxuxuwvuxuwtwvxvyv{t|s}s|u{vxwvwuuuuuswtxtytyvyvyuytysztyuzuzu{r{rzrztyuxvvwswqwtxvxwwwvyuyuy|}~~{{xzuzsyvyxx{{~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~~~~~}~~€€€€€€€€~~~~~~~~~~~~~|~|~~~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~{wzt{u}y~{~z}w}t}s|x|yyzx|x~v~v~w}x}x|w{wzvzwzxzyyzxzxyzx|x}w{wzwxwxwxxzw{w|w}w}x|w{v{u{v|w|w|w|x|x{y{xxxxwxwzx|y}x}w}v|vz}||z{yzy|{|{|{||}|~~}|xyuzw|z|{{}}~}}{yvpuvyvzwyvyuywyvt{x~€€€€€€€€€€€€€€€€€€€€€€€}}x|t{uytpsozs|s|t{t{u{t|u{vzuxuwtwuxuyuzuxtxvwwxwxwxxxxxwyvyvywxxwyuyuxwuzt{uzvywwwwvwuxtyuxvwwwwwuxtxtvuuuvuwuxuwtvtvtvvxvxvxtysxrxswtwuuvruovrwvvxuyt{szvy}~}}{{wzuywxyx{{€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~~}{~}€€€€€€€€~~~}~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€}}z{zz}|}|~|~|zx~z}|{~zz|{yzyxywyxy{z{zzzy{yzyzxzwxvxwxxyyzyzyzyxzxyxyyxzx|x}x~x|xzxyxz{z|{}||||||{|{}|}|}}|{yxwzw|y{{{}}~}|{xupsuwvwwvwwwyxyvv|y~€€€€€€€€€€€€€€€€€€€€€€€~|}w}s|twrqvq{uzuxwvwvwvvxuyuzvyvwuwtysysytxuwvwvwvwwwvyuzv{vzwzw{wyxzxywzu{uztzuzwywxvwuxvwwxxxxxwxvyuxtwtvtwtyu|uzuxuvuvvyvzvzvzuztxtysytxuwsrurwuvwvxvvuuww{{~~|zxyuxvxxx||€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~{~€€€€€€€€€€€€€€~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~€€~|{xyx}yz|{~z|y|z{{z{yzyyzxyzxzx{yzzxzwzvxwyyx{x|x{xxxxyzyvzy|{}|}}}|}|}|~~~~}{zzzx{y{{{~~~~|ztvjxrztxuwuyu{t|ry{y~~}z}u}t}uysvxuzwxuvvsyryrxuwxvywyxxxyv{u{t{u{sztxuxuvvwuyt{t|u|v|w}w|v{v{tzsytytzt}v~v~v}v|w|x{w{uztzu{uzuzuyuyu{w{vyvxuwtxuzu{u{tytysyrzq{tzwyxtxtxwwwvwvvvwwxrw}~~wzw{uzuzvy{}~|~~~~~~~€€€€€€~~~}{{y|{|}{~z~x|v|vz{xxy{zyy{zszx{z}|~}~~~}}|~}~~~~}|zzz{z}|~}|{zurpmxszuwvvvyu{u{sv{z~~~y~u}t|wxwu{u|uysxsvutvswuuwwxxwxwxyxzxzwywyvztztyuwwwwwwywzw|w~wv~vzuytwtwuxwywzv{v|w{w|x{wyvyuyuzt{u{vxuxuxtzu{uxvxuwwxwzv{uztxrwrwqyr{v{zy}xzyx{w{v{uytyuzpx{|x{xzxzxywwz|}|}|}}~||~~~}~}~~~~~€~~~~z|u}s{x|{}}}|}|~}~}~}~}}|{|{|{|x|q|i{jzsxvxuxvywywxrxr|y€€€€€€€€€€€€€€€€€€€€€€€}~x~t}tzvuwtzvzvyuxuxuxuwuwvwwxwxwxvywywyxxxywzvzuyuxvxvxwwwwvxvzvzuzvwwuvvvxwywywyuzuyvxwwxuyrxtxuwxvyvxtwsvsxtytzuyvxvxxxwywzvyuytyszs{u}v|yzxzwzv{v{w{wzuyuxts{{yywwwyxywv{|€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~}~{}z~|~}~€€€€€€€€€€€€€€€€~€€€€€~~}}|}|}}~}~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~xrzx|{}|}|}}}|}|~}~~}}z|u}mhk}pzsxuwvvwwxxwwpxr}z€€€€€€€€€€€€€€€€€€€€€€€|~w|s{tystrswuwvwxvxxxywywywyvywywxwzv{v{wzwzwzvyvzuztysxtwtwtwuwtxuwuwuvuxu{u|uzvxvwwvvvvvvwvwuxuytytytztzsysytytzuzuyuyu{t|uzuzvzu{tzuzwzw{w|r{qzuyvywyuxtwuxvw}}{xxuxuzvzt~|€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~|}~~~|~~€€€€€€€€€€€€€€€€€~€€~~~}|{|{}}~}~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€vqyw||~~}}}||{}|~}~~~~}}y|p}i~k}qytwuvuwuwvvwuxvxurxu~|€€€€€€€€€€€€€€€€€€€€€€~{~w|t{twqsstxuyvxxxxxxxxyxzw{w{wzxyx{w|v}vzwxxxwyv{u{tzuxuvvvvuvuvuwwvwvwtys{s{tyvwxvwuuvsxrzs{s|s{sysxszr{s{s{tzs{t{tzuzu{s{t|u{u{wzvyvywzw|v|uzsvwtwuvvtxsytyxx}}€~|{rzqzsys~}€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~~}{|€~€€€€€€€€€€€€€€€€€€€€€€€~}}||{|{}|~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€xuzx||~}~}|{|z|{}}~~~~}{|u{k}f}j|pztwuvuuwvwvvvuwuvuurxw~}€€€€€€€€€€€€€€€€€€€€~~z~u|t{uurptsvuwvxvxwxwxwyvzuzvyvxvxvyvyuyuwtuusuuvwuzuyvvwuxuvvvuwtwuwvvxsyrztzuywwwvvutvtxsztzs{syswtwuwtxtxszsysztzt{t{t{s{s{t{t{uyvyvyvzv{t|r}n{sxuvwuwwwzv{w{}}~{ttwvxwxx~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~{z€|~€€€€€€€€€€€€€€€€€€€€€€€€€~~||{|{||~}~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€zx{z}|}|}}}|}|}|}||{zwzozg}h}n{rxuxxwyxxxxwxvwuvwtxsyq|w}€€€€€€€€€€€€€€€€€€€€}yv~u{uuqqtuwuxtxswtvvuxuxvxwxvwtxtwuwuvwtxtwuvvuvuuvuuvuvuwvvwvxvwuvuvuwtxtytyuxuwvuuvuuvuvvwvwvvvvuuvvwvwxxwyvxuyvyxwxxwyv|t}t|u{uzvzvyvxuwuxrzozswuvvwwyv|u|v|v{~~~}uuwvzv{y€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~€}€€€€€~}~{~~€€€€€€€€€€€€€€€€€€€€€€€€€€€~~{}z|z|{}|~}€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€|{}|}|}|}}}|}||{{xzqxg{b|k{qyuwuwvxxwzyyyyvyuxtxwwysyr|x~}€€€€€€€€€€€€€€€€€€€€~{xw~x|vwrvuxxvxuytxtvwtytzvzxyvxuxtxuxwwxvzv{wzwwvuuuuuvvwvxvxxwyuytwuuvtvuvwuzu{uyvwuvtvuxuxvwvwvwwwwwxwxvxvvxuxuxvxxvxvwvuxtytzvyu{v|w{wxwxwxuzryuxwwxxx{v|t}u|s{|~~xuyt{s|y€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~}~€€€€~}~€€€€€€€€€€€€€€€€€€€€€€€~~}{|y|z|z}|~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€}|}}~}~}~}~}}|~vlcbj{ryvxvxuwuwuwuyuxvwxxxxxyvyrwq{x~}€€€€€€€€€€€€€€€€€€€€~~z~w}w|xytsrswvxvwxwxvxuyvxwxwxxxvwuwwwyv{v|v{wxyvyvxvuwvvwuwuwvvxvyvxvwvuvuvuwvwxwxwwwwwxvzt{tztztzuytyuxwwwwvwsyq{r{syuwwwvwtxtxtxvxuyu{u{tzs{t|s}pztyuxxyyzxyvywyuxz}yxyxyu{z€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~|~~€€€€~€}€€€€€€€€€€€€€€€€€€}~{}z|y|z}}~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~}~~~~~}}||y|si‚e‚g~o|syuyvxwwvwvvvwvwuxtyuywzwytwnvozx~}€€€€€€€€€€€€€€€€€€~~y}w}v{vwrqrswwxxvxvwvvvvxwyuxuvvsxrxtwxvyuxvwxuytyvwxuytxtxuxtxtxtyvxvxvxuxuxuwtuusutuwuyu{u{uzvyuytys{r|t{vxuwuxs{s|s{tzuzuzt{tztzvywzwzuytwuxvzuzpyuxwwyw}u}tzuxwvy{~|{{y{w|{€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~|~€€€€€€€~€}€~€€€€€€€€€€€€€€€€€~~|}z|z|{}}~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~~~~||v{m|g~h~m{rzvyvzwzvzvyuwvvxvyvyvxvvvvuwuvtqxq{x~}€€€€€€€€€€€€€€€€}~y}t|u{wxouovuwvwvwvwuvvvwuxtwuvvuwswtwuvuuuvwvxwwxvwvuwtxsytxtwtxuwwwwwwxvzt{s{rytwtutvtyuzuzuzuztyrzrzr|t|vywxvxwyv{v|u|t|t}u}uzuzvyvxvxwwwvxwxwwvuwuuwrzt€u‚rs~s}t}|€€}~z}x}|€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€}z~|~~€€€€€~€€€€€€€€€€€€€€€€€~|}z|z|z}|~}~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~|}vzl{h{ozrytytytyuwvxvxuxtwuwvwwwywxuvtutuvtuoyr|y~}€€€€€€€€€€€€€€€€€|~w~t}v|xymyjypytxvyvyvxuwuuwuxtxuyvyvyvxvvvvwwwyuxuwuxvxvxtxtxswtvuuxuxvxvwwxxwxvwuwuwuxuxvxuxvwuwtwsxsyt{t{wzxyxxxyvzuzs{r{sztzvxwwwxvxqyowrvyy}}~}}|}|~|}€~~€~|x~€€~zyyw|}€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~{€{€~~€€€€€€€€€€€€€€€€€€€€€€~}}{}z}z}}~~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~}||u|j}i}o{tytzszpyqytxvvwvvwuwtxtyvzvzvytwtxtxpwjyp{y}|~€€€€€€€€€€€€€€zwu~x|wxlymyryuxwywxwyxxxwyvxuwvxvwxwyvyuyvyxxyvyuxuxuxvzuzuztxvwvwvxuytzuyvywxwwwuxuxvwxvyvyvxuytytxtxvxwxxyxzw{v{uztytxtysytzu{w{t|pysxx{}|~}~~~}~}~~€€€€€€€~|x|w~}€€€€€€€~}}|~~€€€€€€€€€€€€€€€€€€€€€€€€€~€}€}~€~€€€€€€€€€€€€€€€€€€€€€€€€€€€~|~z}z}{}{~}~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€|zyvvowmzp|s{uzwyvysyrytyvxwwuwrwpyrytyvytyrzr{rzmwjwpzy|{~~€€€€€€€€€€€€€€~~z~w~v}xzvssrwuxwxwxwxvxwywywxwvvuwuwuytyuyvxxv{v|vzvyvxuxtztztzuxvwvxvxtxsyrzr{t{wxwwvuwuvvwwvyu{tzsysxtxuywyxyxywyvyuytxtwtwtwuyu|q{szy{}}~~~~~~~}}{|x{xz{}~~€~€€€€€€€~€}~€€|yyv~}€€€€€€€€€€€€€€€€€€€€€€€€€€€€}€||€~€€€€€€€€€€€€€€€€€€€€€€€€€€}{|yzy{y{z}|~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€|o{kxlwqwqyozrzvyxywxvywzwzvysxrxpxqyszszpyoyn{myhvjwrzy|z}|~~€€€€€€€€€€€€€€~}}y}v|v|xyvswsywywxxwwxvxvxwxwwxvwwvxvyvzvzvyvyuzuzvzuzuztztzuyvyvyvzt{uzuvttsurwszuxtwruqurvtvuxtytxsxrxrys{uyvvvvvuuvuwsxsyszuxxuurvv{{~~~~~}~{}xzvxvyvyqykziwqtyo}m€s{€€€€~~~}~~€~~~~~}~}|}~€€€€€€€€€€~~€€€€€€€€€€~€}€{|~~~€€€€€€€€€€€€€€€€€€€€€€~~}||z{y|{||}|~}~€€€€€€€~~~~~~~€€€€€€€€~~~~~~~~~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€xkzl{myqyqyqzr{uzwyvxuxuxvxuwsvsvtwtytytyswswpxkyexkzu|y|z}|~~€€€€€€€€€€€€€€~|~x~v}w|xztwtxwxxvwvxtxuyuzvzwyxxxxxyxyxzxzwyvyuwuyu{u{uyvxwwyxxyxzw{u{tztxsusvrwsytxswsvrxsytytyuxuxuytyryqzryryryrxrxswsxsyszuyxpzu||~~~~}{}q€i{nqtnrrqwqypzmylynuqqvqyv|}€€€€~~€~~~~~}~z}w|w|{}}~€€€€€€€€~€€€€€€€€€€€~~}~}~~~~€€€€€€€€€€€€€€~~€€€€€~}}{|z|z|{}|~}~~€€€€€€€€€~~~~€€€€€€€€€€€~~~~~~~~~~~~~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~~~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€uxwwyvzt{r{r{q{rztytuttttstrurutvwvwvwwvwvwwwvvstlwo{w|{}{~}}}x}u|w{yxrxqyvywvvswqxqzrzt{uzwxxwwvwuwwwwxwuxuyu{v|t{tyuwvwwwvxvywyvysyq{pzryrwuwuvtwszr|t}v|u{vzvxuysysyrzrzsytxuwuxsyrzszvxzszt||~~{xxm}l{qvwo}h|iypwuvuvwtwpxpxqzv{{~~~{}v}u|z}|~}~~~}{|y|z}|~}~~~xwwwxwxtzr|o{m|n{pxrssovoupututvwwxuwuwrxrxuwvuwqqtrzx|{{{~~€€€€€€€€€€€€€€€€~}|w{t{wzxvpvpyuyvvvrvpxoyqzsyuxvxvwuutsvtxtxuvvuxvzv{tzqyozqzrzr{t{vywyszp~nq{tvwtvstvr{p|r}t{uyvxvwtwrwsxtwuwvwvvvvvwrzp~s}{x‚lƒw€}rh€m{rxp{i_…_‡d†lp|vuys{q}s}y~}€€€€€€€€€€€€€€€€}{yuyt}{~}~}€€€€€€€€€€€€€€€€€€€€€€€€€€~~€€€€€€€€€€€€€€€€€€€€€~|{{y|z}|~}~€€€€€€€€€€~€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~~}€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€ diff --git a/merrifield/test/nv12_ved_test.cpp b/merrifield/test/nv12_ved_test.cpp new file mode 100644 index 0000000..7fb5b36 --- /dev/null +++ b/merrifield/test/nv12_ved_test.cpp @@ -0,0 +1,147 @@ +/* +// Copyright (c) 2014 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#include <gtest/gtest.h> + +#include <binder/IMemory.h> + +#include <gui/ISurfaceComposer.h> +#include <gui/Surface.h> +#include <gui/SurfaceComposerClient.h> +#include <private/gui/ComposerService.h> + +#include <utils/String8.h> + +using namespace android; +const char * filename = "/data/my_640x480.nv12"; +#define PIXEL_FORMAT_NV12 0x7FA00E00 + +// Fill a YV12 buffer with a multi-colored checkerboard pattern +void fillYUVBuffer(uint8_t* buf, int w, int h, int stride) { + const int blockWidth = w > 16 ? w / 16 : 1; + const int blockHeight = h > 16 ? h / 16 : 1; + const int yuvTexOffsetY = 0; + int yuvTexStrideY = stride; + int yuvTexOffsetV = yuvTexStrideY * h; + int yuvTexStrideV = (yuvTexStrideY / 2 + 0xf) & ~0xf; + int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * h / 2; + int yuvTexStrideU = yuvTexStrideV; + for (int x = 0; x < w; x++) { + for (int y = 0; y < h; y++) { + int parityX = (x / blockWidth) & 1; + int parityY = (y / blockHeight) & 1; + unsigned char intensity = (parityX ^ parityY) ? 63 : 191; + buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = intensity; + if (x < w / 2 && y < h / 2) { + buf[yuvTexOffsetU + (y * yuvTexStrideU) + x] = intensity; + if (x * 2 < w / 2 && y * 2 < h / 2) { + buf[yuvTexOffsetV + (y * 2 * yuvTexStrideV) + x * 2 + 0] = + buf[yuvTexOffsetV + (y * 2 * yuvTexStrideV) + x * 2 + + 1] = + buf[yuvTexOffsetV + + ((y * 2 + 1) * yuvTexStrideV) + + x * 2 + 0] = buf[yuvTexOffsetV + + ((y * 2 + 1) * yuvTexStrideV) + + x * 2 + 1] = intensity; + } + } + } + } +} + +void loadYUVBufferFromFile(uint8_t* buf, int w, int h, int stride) { + FILE *fp = fopen(filename, "r"); + int line = 0; + int offset = 0; + int buffer_height = h * 1.5; + + if (!fp) { + printf("%s: failed to open %s\n", __func__, filename); + return; + } + + printf("buf=%p, w=%d,h=%d,stride=%d\n", buf, w, h, stride); + + for (line = 0; line < buffer_height; line++) { + printf("reading line %d...\n", line); + offset = line * stride; + fread(buf + offset, w, 1, fp); + } + + fclose(fp); +} + +int main(int argc, char **argv) { + sp < SurfaceControl > sc; + sp < Surface > s; + sp < ANativeWindow > anw; + ANativeWindowBuffer *anb; + uint8_t* img = NULL; + sp < SurfaceComposerClient > composerClient = new SurfaceComposerClient; + if (composerClient->initCheck() != NO_ERROR) + return 0; + + sc = composerClient->createSurface(String8("FG Test Surface"), 640, 480, + PIXEL_FORMAT_RGBA_8888, 0); + if (sc == NULL) + return 0;; + if (!sc->isValid()) + return 0; + + s = sc->getSurface(); + anw = s.get(); + if (native_window_set_buffers_geometry(anw.get(), 640, 480, + PIXEL_FORMAT_NV12) != NO_ERROR) + return 0; + if (native_window_set_usage(anw.get(), + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN) + != NO_ERROR) + return 0; + + /* + * load buffer + */ + if (native_window_dequeue_buffer_and_wait(anw.get(), &anb)) + return 0; + if (anb == NULL) + return 0; + sp < GraphicBuffer > buf(new GraphicBuffer(anb, false)); + //if (anw->lockBuffer(anw.get(), buf->getNativeBuffer()) != NO_ERROR) + // return 0; + buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**) (&img)); + if (!img) { + printf("failed to lock buffer\n"); + exit(-1); + } + + loadYUVBufferFromFile(img, 640, 480, buf->getStride()); + buf->unlock(); + printf("querying buffer...\n"); + if (anw->queueBuffer(anw.get(), buf->getNativeBuffer(), -1) != NO_ERROR) + return 0; + + // loop it to continuously display?? + while (1) { + SurfaceComposerClient::openGlobalTransaction(); + if (sc->setLayer(INT_MAX - 1) != NO_ERROR) + return 0; + if (sc->show() != NO_ERROR) + return 0; + + SurfaceComposerClient::closeGlobalTransaction(); + } + return 0; +} + |